mirror of
https://github.com/bevyengine/bevy.git
synced 2026-06-30 15:55:32 -04:00
create-pull-request/patch
257 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6f270d4776 |
Expose pipeline constants to materials (#24502)
# Objective Allow materials to use pipeline constants ([pipeline-overridable constants](https://www.w3.org/TR/WGSL/#override-decls)). They are already available in wgpu, but bevy didn't expose them. ## Solution Expose constants in `RenderPipelineDescriptor` & `ComputePipelineDescriptor`, allowing materials to specify them in their specilize function. **Note:** I had to remove the `Eq` derive from `ComputePipelineDescriptor`, `VertexState` and `FragmentState`, due to the new f64 field. It was already a bit inconsistent with `RenderPipelineDescriptor` not having it. ## Testing - Ran `cargo check` - Created an example & ran it - Couldn't run `cargo test` due to it taking looots of disk space to run, but I have a hard time seeing it break something at runtime ## Showcase See the added example, where pipeline constants are used to change the `LEVELS` override in WGSL. <img width="760" height="289" alt="Screenshot from 2026-05-31 09-46-05" src="https://github.com/user-attachments/assets/6902757c-aea4-4b91-9ff0-e653ce4c3448" /> |
||
|
|
7517c61ecd |
Add options to lower precision/compressed vertex buffer (#21926)
# Objective Resolves #21902. ## Solution This PR adopts a relatively transparent approach to reduce the GPU vertex buffer size. On CPU-side mesh can still use uncompressed Float32 data, and users are not required to insert compressed vertex formats. The vertex data is automatically processed into lower-precision/octahedral encoded data when uploading to the GPU. To enable vertex attribute compression, just set the `attribute_compression` field of Mesh, or set `mesh_attribute_compression` of GltfLoaderSettings. If enabled, normal and tangent will be octahedral encoded Unorm16x2, uv0, uv1, joint weight and color will be corresponding Unorm16 or Float16. I also provide Unorm8x4 for vertex color if hdr isn't needed. Update 2026-2-16 Removed previous approach that automatically compresses vertex buffer according to flags when uploading to GPU. Instead, I added `compressed_mesh` method to Mesh to construct compressed Mesh ahead of time. GltfLoader can also opt-in mesh compressing when loading. I also add an option to convert indices to u16, though I believe blender gltf exporter already uses u16 indices when possible. ## Testing Run `many_cubes`, `many_foxes`, `many_morph_targets` with `--vertex-compression` to test 3d. Run `bevymark` with `sprite_mesh` to test 2d, because `SpriteMesh` uses compressed quad mesh now. --------- Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com> |
||
|
|
5ec83e1185 |
Improve Order Independent Transparency example (#22781)
# Objectives - Use a cleaner UI inspired by other examples - Add a scene with custom material to fix #20297 ## Testing Running the example ## Showcase <details> <summary>Click to view showcase</summary> <img width="872" height="732" alt="Screenshot From 2026-02-06 11-35-19" src="https://github.com/user-attachments/assets/cc294c33-bb53-4ed4-9dce-7558f3bb8fee" /> <img width="872" height="732" alt="Screenshot From 2026-02-06 11-35-30" src="https://github.com/user-attachments/assets/c2b79651-c99e-4bcc-8089-518d11631b9f" /> </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
afd576f380 |
Alpha discard (#11895)
# Objective I want to call the `pbr_functions::alpha_discard` shader function from my own material shader, but it only takes a `StandardMaterial` parameter. ## Solution This PR replaces the `StandardMaterial` parameter with less restrictive ones so my shader can call it. --- ## Changelog - The signature of `pbr_functions::alpha_discard` changed by replacing `StandardMaterial` with only the `flags` and `alpha_cutoff` fields. ## Migration Guide Replace this: ```wgsl pbr_functions::alpha_discard(material, ...); ``` with this: ```wgsl pbr_functions::alpha_discard(material.flags, material.alpha_cutoff, ...); ``` --------- Co-authored-by: Duncan Fairbanks <duncanfairbanks6@gmail.com> |
||
|
|
489818930b |
Update ktx2 to v0.5.0 (#23900)
Updated ktx2 crate. After doing so, Bevy ktx2 files failed to validate due to invalid metadata. I fixed all their metadata, and made sure they passed ktx validate. |
||
|
|
535cf401cc |
Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene system (often known as BSN), which now lives in the `bevy_scene` / `bevy::scene` crate. However the old `bevy_scene` system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!): 1. It is not _yet_ possible to write a World _to_ BSN, so the old system is still necessary for "round trip World serialization". 2. The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy. For this reason, we have renamed the old `bevy_scene` crate to `bevy_world_serialization`. If you were referencing `bevy_scene::*` or `bevy::scene::*` types, rename those paths to `bevy_world_serialization::*` and `bevy::world_serialization::*` respectively. Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization": - `Scene` -> `WorldAsset` (as this was always just a World wrapper) - `SceneRoot` -> `WorldAssetRoot` - `DynamicScene` -> `DynamicWorld` - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset` - `DynamicSceneBuilder` -> `DynamicWorldBuilder` - `DynamicSceneRoot` -> `DynamicWorldRoot` - `SceneInstanceReady` -> `WorldInstanceReady` - `SceneLoader` -> `WorldAssetLoader` - `ScenePlugin` -> `WorldSerializationPlugin` - `SceneRootTemplate` -> `WorldAssetRootTemplate` - `SceneSpawner` -> `WorldInstanceSpawner` - `SceneFilter` -> `WorldFilter` - `SceneLoaderError` -> `WorldAssetLoaderError` - `SceneSpawnError` -> `WorldInstanceSpawnError` Note that I went with `bevy_world_serialization` over `bevy_ecs_serialization`, as that is what all of the internal features described themselves as. I think it is both more specific and does a better job of making itself decoupled from `bevy_ecs` proper. |
||
|
|
45e454a83b |
Rename bevy_scene to bevy_ecs_serialization (#23619)
# Objective The first part of #23606. ## Solution Rename `bevy_scene` to `bevy_ecs_serialization`. No other changes were made. ## Testing The `scene` example still works as expected. |
||
|
|
2e2b29eb33 |
Allow serializing and deserializing handles during reflection. (#23329)
# Objective - Partly addresses #15518 - this is a big issue but the handle part is all resolved! - Make the existing scene system much more useful! ## Solution - Create a `ReflectSerializerProcessor` and `ReflectDeserializerProcessor` that "hijacks" the ser/de of handles to just write out the path being loaded (or the UUID), and loads the assets on deserialization. - This was made possible by #15482! Very useful! This *should* also be composable with other processors by just creating a wrapper that calls both processors in series. - Make the scene ser/de use this processor by default. Now handles just work in scenes! - Update the scene example to show a scene that includes a scene! Nested scenes!! I've left a TODO that the scene system could expose the `EphemeralHandleBehavior` so users can choose to be more strict. It's probably not a big deal to solve IMO. Also I had to call it `AssetServer::load_erased` since we already have an `AssetServer::load_untyped` which tries to guess the asset type based on the path. `AssetServer::load_erased` in contrast knows what type it wants, just at runtime. It feels like we should have a less adhoc API here, one where we know we've covered the cross product of all features. Something to think about! ## Testing - Added a test for the processors that they can roundtrip a type with handles. - Added a test that the scene system can roundtrip components with handles. - Updated the scene example and it spawns with the handle correctly! |
||
|
|
acd1091c3e |
Add Martian atmosphere constant (#22884)
# Objective - Generally hard to customize meter-based metrics for atmospheres - Address user concerns described in issue #22882 for creating a mars atmosphere ## Solution - Provide a default for mars like atmospheres, a common use case for a good base to start with. - The values in this pull request are based on this paper: https://www.researchgate.net/publication/380239530_Physically_Based_Real-Time_Rendering_of_Atmospheres_using_Mie_Theory - I created a pre-computed lookup texture for the mie scattering and enabled the use of such textures within the atmosphere's `medium` struct. note that this is only 16kb and doesn't weigh much in the repo. however it could be moved to an asset on the network. - Full derivation of the texture and constants can be found in this repo I created: https://github.com/mate-h/mars-atmo ## Testing - Updated the atmosphere example --- ## Showcase <img width="1280" height="723" alt="Screenshot 2026-02-14 at 1 20 35 AM" src="https://github.com/user-attachments/assets/3487c67d-ce46-428e-a62b-88f6d9accbc6" /> <img width="1278" height="719" alt="Screenshot 2026-02-14 at 1 18 44 AM" src="https://github.com/user-attachments/assets/2613182d-27e6-41d6-85fd-57cf40277909" /> <img width="1276" height="721" alt="Screenshot 2026-02-14 at 1 17 41 AM" src="https://github.com/user-attachments/assets/cedb8d51-74eb-4c1c-9aaf-1994f6776b88" /> <img width="1278" height="721" alt="Screenshot 2026-02-14 at 1 13 53 AM" src="https://github.com/user-attachments/assets/357a6907-1012-401b-857b-107f20c24fdd" /> <img width="1281" height="721" alt="Screenshot 2026-02-14 at 1 12 30 AM" src="https://github.com/user-attachments/assets/aac10fed-d06f-4fad-9057-4ea5634d7a78" /> <img width="1279" height="722" alt="Screenshot 2026-02-14 at 1 09 26 AM" src="https://github.com/user-attachments/assets/7ddb3663-e6dd-413f-a771-26ae620da480" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: atlv <email@atlasdostal.com> |
||
|
|
1b0f112d90 |
Implement GPU clustering for lights, light probes, and decals. (#23036)
Currently, Bevy clusters lights on the CPU. This is generally not considered a best practice any longer, and it can be a bottleneck in workloads like `many_lights`. Moreover, it prevents GPU systems like [Hanabi] from creating clusterable objects such as lights and decals without a round trip to the CPU. This PR introduces GPU light clustering when supported by the hardware. The algorithm is the same as the existing GPU light clustering, but parallelized over all clusters, and the resulting on-GPU format for clusters is unchanged. GPU light clustering uses the hardware rasterizer for compute purposes as a way to automatically distribute workloads within 2D axis-aligned bounding boxes without actually rendering any pixels, a first for Bevy. The algorithm is as follows, with each step corresponding to a raster or compute command: 1. *Z slicing*: We have a 3D cluster froxel grid of size WxHxD and seek to rasterize D axis-aligned quads, each of size WxH, representing the range of each clusterable object. In this compute phase, we generate D indirect instances for each clusterable object for the subsequent indirect draws. 2. *Count rasterization*: We use instanced indirect drawing to rasterize each quad generated in step 1 to a viewport of size WxH, with color writes disabled. Each rasterized fragment represents a cluster-object pair. In the fragment shader, we check to see if the object intersects the cluster, and, if it does, we atomically bump a counter corresponding to the number of objects of the given type intersecting the cluster in question. We don't record the ID of the object in this phase; we simply count the number of objects. 3. *Local allocation*: Now that we know the number of objects of each type in each cluster, we can proceed to allocate space in the clustered object buffer for each clustered object list. To do this, we need to perform a [*prefix sum*] operation so that each list is tightly packed with the others. For example, if adjacent clusters have 2, 5, and 3 objects, they'll be allocated at offsets 0, 2, and 7 respectively. This *local* step uses a [Hillis-Steele scan] in shared memory to compute the prefix sum of each chunk of 256 clusters. We can't go beyond 256 clusters in this local step because 256 is the maximum workgroup size in `wgpu`. 4. *Global allocation*: To deal with the fact that we can't calculate prefix sums beyond 256 clusters in step 3, we employ this second step that does a sequential loop over every 256-cluster chunk, propagating the prefix sum. At the end of this step, every list of clustered objects is allocated. 5. *Populate rasterization*: Finally, we issue an instanced indirect draw command using the same parameters as step (2). We test each cluster-object pair for intersection, and, if the test passes, we record the ID of each clustered object into the correct space in the list, using a scratch pad buffer of atomics to store the position of the next object in each list. The buffer of clustered objects has a fixed size and can overflow. We detect this condition via asynchronous CPU readback and automatically grow the buffer for subsequent frames. In this case, we also log a message so that the developer can choose a larger initial buffer size and avoid any incorrect frames. Additionally, like #22874, the automatic clustering heuristics are dynamically adjusted from frame to frame, by recording statistics on the GPU and using CPU readback to download them back to the CPU for processing. As part of this PR, I refactored clustered visibility so that clustered objects go through the same `ViewVisibility` system as other objects, instead of using `VisibleClusterableObjects`. This was a nice simplification. On the `many_lights` benchmark, with about 8,000 lights visible out of 100,000, this process takes approximately 0.099 ms on my NVIDIA GeForce RTX 4070 Laptop GPU. The AMD Ryzen 9 8945HS CPU, however, takes 2.12 ms to do the same task. The GPU version is therefore a 21x speedup. `main` `assign_objects_to_clusters` time, 2.12 ms: <img width="2756" height="1800" alt="Screenshot 2026-02-17 222757" src="https://github.com/user-attachments/assets/66341ad2-96f2-4e4a-87ee-fe3462bc05de" /> GPU clustering GPU time, 0.099 ms: <img width="2756" height="1800" alt="Screenshot 2026-02-17 222458" src="https://github.com/user-attachments/assets/18e2e0ae-a946-4b80-b38a-0543e76ebc02" /> `main`, 5.71 ms median frame time, 175 FPS: <img width="2756" height="1800" alt="Screenshot 2026-02-17 222243" src="https://github.com/user-attachments/assets/111c8e22-414f-4ee1-95fa-d7cfe422c2ab" /> GPU clustering, 4.88 ms median frame time, 205 FPS: <img width="2756" height="1800" alt="Screenshot 2026-02-17 222256" src="https://github.com/user-attachments/assets/0a662e88-a1b9-49c8-8bab-cc12b46cd079" /> [Hanabi]: https://github.com/djeedai/bevy_hanabi [*prefix sum*]: https://en.wikipedia.org/wiki/Prefix_sum [Hillis-Steele scan]: https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel ## Alice's PM Note from @kfc35 Fixes https://github.com/bevyengine/bevy/issues/22957 and also fixes https://github.com/bevyengine/bevy/issues/22904. |
||
|
|
5e1630bfd8 |
Fix 16 byte alignment typo (WebGL 2: 16 bit -> 16 byte) (#23124)
# Objective WebGL 2 requires 16 **byte** UBO alignment. Some comments incorrectly state 16 **bits**. ## Solution Fix comment typos. ## Testing N/A --- ## Showcase N/A |
||
|
|
87c9a5a0ef |
Support Tile transforms in TilemapChunk (#22889)
# Objective Fixes #22888 ## Solution - Add an `orientation` field to `TileData`. This contains a `TileOrientation` enum representing the tile being rotated and/or mirrored. This defaults to the current orientation (tile is not rotated or mirrored). The orientation is packed into the `flags` of `PackedTileData` as 3 new bits alongside the existing `visible` boolean. The bits represent mirroring horizontally (left and right swapped), mirroring vertically (top and bottom swapped), and diagonally (top-right and bottom-left corners are swapped). Together, the 8 combinations represent all possible combinations of 90 degree rotations with mirroring in the X or Y axis. - Update `tilemap_chunk_material.wgsl` to unpack the bits, and use them to modify the `local_uv` as needed to apply the transform to the tile. `TileOrientation` also provides some convenience methods for construction, and for treating the orientation as a transform that can be inverted and combined, as well as applying that transform to an `IVec2`. There is a new `tilemap_chunk_orientation` example showing all orientations, combined with tile indices, color, alpha and visibility to check packing of data. ## Testing There are some simple tests on the `TileOrientation` itself (e.g. checking that combining an orientation with its inverse produces the default orientation, applying each orientation as a transform to a test `IVec2` to confirm the result is as expected based on manually worked out expected results, and that inverses will map a point back to its origin). Rendering can be tested using the new example, I've tested this on a Macbook M1 Max. --- ## Showcase The new `tilemap_chunk_orientation` example uses one tileset entry for each tile on each row, showing the 8 transforms along the row: <img width="687" height="707" alt="Screenshot 2026-02-23 at 14 25 29" src="https://github.com/user-attachments/assets/9cb20236-d180-490c-bd3f-f6e61c30c099" /> This also shows different colors and alpha values on each of 8 rows to check they work alongside transforms, the top row is set to invisible. The `tileset_index` alternates per row. This uses a simple two-tile tileset, shown below scaled up by 4: <img width="32" height="64" alt="arrow" src="https://github.com/user-attachments/assets/5536beb9-7a3e-4ec0-a667-777ba8ad283a" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
71ce303ec2 |
compute-shader mesh generation example (#22296)
# Objective People have been asking how to get a compute shader-built mesh into bevy's "stuff". Some people want to control the lifetime of the mesh via Handle, and others don't don't how to set data in bind groups. ## Solution a new example that shows how to initialize a mesh handle with a render_world usage mesh, and then put the output of the compute shader into the mesh_allocator slab for the mesh. The demo creates a scene with a camera, light, a circular base mesh, and an empty "cube to be" mesh that is shared by cloning the handle across two entities. The compute shader then fills in the data directly into the mesh_allocator slabs for the vertex/index buffers. If the compute shader failed, there would be no cube meshes showing as the data would be empty. ## Testing ``` cargo run --example compute_mesh ``` --- ## Showcase <img width="3392" height="2106" alt="screenshot-2025-12-29-at-16 06 48@2x" src="https://github.com/user-attachments/assets/88d8fed4-e3c1-418e-bb04-6f08d673403a" /> |
||
|
|
4189ba072d |
Provide a mechanism for applications to invoke the single-pass downsampler. (#22286)
The [AMD FidelityFX single-pass downsampler] (SPD) is the fastest way to generate mipmap levels of a texture. Bevy currently has two separate ports of that algorithm to WGSL: one for use in the environment map generation and one for use on the depth buffer for the purposes of occlusion culling (though the latter isn't the best use of it). Absent is any mechanism to use the single-pass downsampler to generate mipmap levels of a color texture for typical use in rendering. This is a standard feature in game engines: for example, Unity has [`GenerateMips`] and Unreal has [`bAutoGenerateMips`]. This PR adds a mechanism by which applications can invoke SPD to generate mipmap levels for any `Image`. Using this mechanism is a two step process. First, the application adds the `Handle<Image>` to a resource, `MipGenerationJobs` and associates it with a *phase*, which is an arbitrary ID chosen by the application. Second, the application adds a `MipGenerationNode` for that phase to the render graph. During rendering, the `MipGenerationNode` invokes SPD to generate a full mipmap chain for all textures in that phase. The reason why mipmap generation jobs are associated with phases is that the generation of mipmaps may need to occur at precise points in the application rendering cycle. For example, consider the common situation of a mipmapped portal texture. The mipmaps must be generated *after* the portal is rendered, but *before* the object in the main world displaying the portal texture is drawn. The phased approach taken in this PR allows complex dependencies like this to be expressed using the node graph feature that Bevy already possesses. (In the future, if render graphs are removed in favor of systems, this approach can naturally be reframed in terms of systems, so this patch contains no hazards in that regard.) Note that this patch by itself doesn't automatically generate mipmaps for imported textures that don't have them the way that [`bevy_mod_mipmap_generator`] does, in order to keep this patch relatively small and self-contained. However, it'd be straightforward to either (a) extend `bevy_mod_mipmap_generator`, (b) write another plugin, and/or (c) add a new feature to Bevy itself, all built on top of this PR, to support automatic GPU mip generation for image assets that don't have them. A new example, `dynamic_mip_generation`, has been added. This is a 2D example that produces a texture at runtime on the CPU and invokes the new `MipGenerationNode` that this patch adds to generate mipmaps for that texture at runtime. The colors of the texture are randomly generated, and UI for the example allows the texture to be regenerated and for the size to be adjusted; this proves that the mipmap levels for the texture are indeed generated at runtime and not pre-calculated at build time. Note that, although the example is 2D, the feature that this patch adds can be equally used in 2D and 3D. [AMD FidelityFX single-pass downsampler]: https://gpuopen.com/fidelityfx-spd/ [`GenerateMips`]: https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.GenerateMips.html [`bAutoGenerateMips`]: https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/DisplayClusterConfiguration/FDisplayClusterC-_33/bAutoGenerateMips [`bevy_mod_mipmap_generator`]: https://github.com/DGriffin91/bevy_mod_mipmap_generator |
||
|
|
b4e74debc8 |
Font weight support (#22038)
# Objective Add font weight support. ## Solution * New `FontWeight` struct that newtypes a `u16`. * New `font: FontWeight` field on `TextFont`. * The weight attribute for the cosmic text buffer is set in `TextPipeline` during text updates. * Added a new font asset, `MonSans-VariableFont.ttf`. This needs a variable font for testing. # Doesn't support `lighter` and `bolder` as that would require text style inheritance, which we don't support yet. I added stretch and slant as well, but split them off from this PR. Swash only has limited variable-font support and there's no way to demonstrate that they work without other changes. ## Testing Added a basic example: ```cargo run --example font_weights``` ## Showcase <img width="671" height="845" alt="font_weights" src="https://github.com/user-attachments/assets/8a8686ac-faa8-442c-89d4-f56348a7e788" /> --------- Co-authored-by: Thierry Berger <contact@thierryberger.com> |
||
|
|
681751647a |
Add FullscreenMaterial (#20414)
# Objective - Users often want to run a fullscreen shader but the current solution involves copying the custom_post_processing example which is a 350 line file with a lot of low level wgpu complexity. Users shouldn't have to deal with that just to make a fullscreen shader ## Solution - Introduce a new FullscreenMaterial trait and FullscsreenMaterialPlugin - This new material will run a fullscreen triangle with the specified shader. It builds on top of the existing FullscreenShader infrastructure - It lets user customize the node ordering. There's no defaults right now becausae it's intended as a bit of a primitive plugin. Eventually we could have some kind of default for custom post processing ## Testing Made a new fullscreen_material example and made sure it works ## Follow up Once this is merged there are various things that should be done to improve it. Add the option to bind the depth texture, offer defaults for post processing, use a full AsBindGroup, add a way to bind the gbuffer. --------- Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
e7b64b6b04 |
Add support for arbitrary/third party glTF Extension processing via GltfExtensionHandler (#22106)
# Objective Currently Bevy doesn't support arbitrary glTF extensions. The ones it does support are hardcoded. We should support glTF extensions, as this is a primary mechanism for sharing behavior via data exported from applications like Blender. I personally have found usecases in exporting component data, lightmap textures/information, and processing other kinds of data (AnimationGraph, 3d meshes into 2d, etc). ## Solution This PR introduces a new `GltfExtensionHandler` trait that users can implement and add to the glTF loader processing via inserting into a Resource. There are two example processors currently added, with a third that I'd like to add after this PR. - `examples/gltf/gltf_extension_animation_graph.rs` duplicates the functionality of `animation_mesh`, constructing AnimationGraphs via extension processing and applying them to be played on the relevant nodes. - `examples/gltf/gltf_extension_mesh_2d.rs` duplicates the functionality of the `custom_gltf_vertex_attribute` example, showing how the extension processing could be used to convert 3d meshes to 2d meshes alongside custom materials. Both of these examples re-use existing assets and thus don't *actually use* extension data, but show how one could access the relevant data to say, only convert specifically labelled Mesh3ds to 2d, or process many animations into multiple graphs based on extension-data based labelling introduced in Blender. A third example I want to introduce after this PR is the same core functionality Skein requires: an example that uses reflected component data stored in glTF extensions and inserts that data onto the relevant entities, resulting in scenes that are "ready to go". ## Comparison to Extras In comparison to extensions: data placed in glTF extras is well supported through the `GltfExtras` category of components. Extras only support adding an additional `extras` field to any object. Data stored in extras is application-specific. It should be usable by Bevy developers to implement their own, application-specific, data transfer. This is supported by applications like Blender through the application of Custom Properties. Once data is used by more than one application, it belongs in a glTF extension. ## What is a glTF Extension? Extensions are named with a prefix like `KHR` or `EXT`. Bevy has already reserved the `BEVY` namespace for this, which is listed in the official [prefix list](https://github.com/KhronosGroup/glTF/blob/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions/Prefixes.md). For a glTF file, an extension must be listed in `extensionsUsed` and optionally `extensionsRequired`. ``` { "extensionsRequired": [ "KHR_texture_transform" ], "extensionsUsed": [ "KHR_texture_transform" ] } ``` Extension data is allowed in any place extras are also allowed, but also allow much more flexibility. Extensions are also allowed to define global data, add additional binary chunks, and more. For meshes, extensions can add additional attribute names, accessor types, and/or component types `KHR_lights_punctual` is a contained and understandable example of an extension: https://github.com/KhronosGroup/glTF/blob/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions/2.0/Khronos/KHR_lights_punctual/README.md . This one happens to be already hardcoded into Bevy's handling, so it doesn't benefit from arbitrary extension processing, but there are additional [ratified](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#ratified-khronos-extensions) and [in-progress](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#in-progress-khronos-and-multi-vendor-extensions-and-projects) extensions, as well as [vendor](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#vendor-extensions) and other arbitrary extensions that would benefit from userland support. ## Implementation This initial implementation is reasonably minimal: enabling extension processing for objects/etc as they're loaded which may also define extension data, including the scene world. This may leave out useful functionality; as detailed in the next section: "What's not implemented". Extension handlers are defined by implementing a trait which can optionally define hooks and data. Extension handler data is cloned to start with a fresh slate for each glTF load, which limits scope to "one glTF load". So while state can be maintained across hooks during a single load, users who want to combine or handle multiple glTF assets should do so in the main app, not in an extension handler. Following this, because the extensions are stored as `dyn GltfExtension` *and* we want to clone them to isolate state to a single load, `dyn_clone` must be included as a workaround to enable this cloning. An extension handler has to be added to the list of handler by accessing a `Resource` and pushing an instantiated handler into it. This Resource keeps the list of extension handlers so that a new glTF loader can bootstrap them. The design of the hooks is such that: - If no extensions handlers are registered, none are called for processing - If an extension handler is defined, it receives all "events" - handlers are defined by a trait, and default implementations are called if an override is not specified. - default implementations are no-ops It is important that extensions receive all events because certain information is not embedded in extension data. For example, processing animation data into an animation graph could require both processing animations with extension data, tracking the animation roots through hooks like `on_node`, *and* applying those graphs in the `on_scene_completed` hook. - Extension data is passed to hooks as `Option<&serde_json::Value>` which is only passing references around as the data has already been converted to `Value` by the `gltf` crate. - `LoadContext` is required for creating any new additional assets, like `AnimationGraph`s. - *scene* World access is provided in hooks like `on_scene_completed`, which allows calculating data over the course of a glTF load and applying it to a Scene. ### What's not implemented This PR chooses to *not* implement some features that it could. Instead the approach in this PR is to offer up the data that Bevy has already processed to extensions to do more with that data. - Overriding `load_image`/`process_loaded_texture` - This could allow projects like bevy_web_codecs, [which currently forks the entire gltf loader](https://github.com/jf908/bevy_web_codecs/tree/373bbf29be6555c7603fd6867a01159ab0f20fed/bevy_web_codecs_gltf). Associated [issue](https://github.com/bevyengine/bevy/issues/21185). However I believe this needs some design work dedicated to what exactly happens here to support that use case. - This PR doesn't include any refactoring of the glTF loader, which I feel is important for a first merge. - ~~There is some benefit to passing in the relevant `gltf::*` object to every hook. For example, I believe this is the only way to access extension data for `KHR_lights_punctual`, and [`KHR_materials_variants`](https://docs.rs/gltf/1.4.1/gltf/struct.Document.html#method.variants) or other extensions with "built-in" support. I haven't done this in all places.~~ (edit: after external implementation I decided this was a good idea and added it to more places) ## Testing ``` cargo run --example gltf_extension_animation_graph cargo run --example gltf_extension_mesh_2d ``` --- ## Showcase Both examples running: https://github.com/user-attachments/assets/f9e7c3c9-cdad-4d33-ace7-7c2ca5469d5e https://github.com/user-attachments/assets/baa9bc92-ca3b-46ad-a3f0-2f74bbc29b68 <details> <summary>An example that showcases converting Mesh3d to Mesh2d</summary> ```rust #[derive(Default, Clone)] struct GltfExtensionProcessorToMesh2d; impl GltfExtensionProcessor for GltfExtensionProcessorToMesh2d { fn extension_ids(&self) -> &'static [&'static str] { &[""] } fn dyn_clone(&self) -> Box<dyn GltfExtensionHandler> { Box::new((*self).clone()) } fn on_spawn_mesh_and_material( &mut self, load_context: &mut LoadContext<'_>, _gltf_node: &gltf::Node, entity: &mut EntityWorldMut, ) { if let Some(mesh3d) = entity.get::<Mesh3d>() && let Some(_) = entity.get::<MeshMaterial3d<StandardMaterial>>() { let material_handle = load_context.add_loaded_labeled_asset("AColorMaterial", (CustomMaterial {}).into()); let mesh_handle = mesh3d.0.clone(); entity .remove::<(Mesh3d, MeshMaterial3d<StandardMaterial>)>() .insert((Mesh2d(mesh_handle), MeshMaterial2d(material_handle.clone()))); } } } ``` </details> --------- Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com> |
||
|
|
d60a1b8166 |
Add MeshTag to array_texture example to demonstrate layer selection in shader (#21989)
## Objective - When looking at the `array_texture` example, it wasn't clear to me how I could send the "layer" to the GPU, but it turns out that [MeshTag is one recommended way](https://discord.com/channels/691052431525675048/866787577687310356/1444495450999754823) to pass this. - The example previously extracted a fake "layer" from the world position, but IIUC this isn't the most realistic way to demonstrate layer selection. ## Solution - Update the `array_texture` example by using `MeshTag`. - Add a system to the example that periodically changes the `MeshTag` on entities to show that the mesh tag can also change dynamically at runtime (and show how easy it is). ## Testing and showcase Before, you can see each cube's texture is fixed. <img width="1280" height="747" alt="image" src="https://github.com/user-attachments/assets/1ffde7db-8110-4431-b4e8-3a5a4ba5c5db" /> After, you can see each cube's texture changes as time passes. https://github.com/user-attachments/assets/b1227659-5886-4d2c-a401-84b80423c798 ---- I'm hoping for a rendering dev to validate this approach is correct, and useful. I think it is, but [I'm only just starting to understand](https://discord.com/channels/691052431525675048/866787577687310356/1444888786478829668) how to use this stuff and it's [possibly not the only way](https://discord.com/channels/691052431525675048/866787577687310356/1444888304020488365) so I don't want to submit this if it's the wrong approach to teach future me's. |
||
|
|
00f6eb7a1c |
Implement the infrastructure needed to support portals and mirrors. (#13797)
Implement the infrastructure needed to support portals and mirrors. Bevy currently supports multiple cameras and rendering to off-screen render targets, so one might naïvely think that the engine has support for portals and mirrors already. However, Bevy is missing two key features that enable portals and mirrors at present: 1. Bevy has support for neither custom clip planes nor oblique clip planes. This prevents the construction of proper portals or mirrors, as meshes that intersect the portal plane must be clipped to render properly. 2. Bevy has no support for cameras that invert the culling mode, so meshes that are reflected across a plane will render inside-out. This PR addresses the two issues above: 1. This commit introduces a new field on `PerspectiveProjection`, `near_plane`, which allows the application to specify a custom near plane. That feature fully enables [Lengyel oblique clipping], which is the most optimal way to achieve a custom near clipping plane. It allows us to avoid having to support custom clip planes, which are often implemented inefficiently in hardware. 2. This patch adds a new field on the `Camera` component, `invert_culling`. This field causes the Bevy renderer to invert the front face setting when rendering the objects visible from that camera. When coupled with an appropriately-set [Householder matrix] on the camera, this allows correct rendering of objects reflected across a plane. Additionally, this PR adds a new function to `bevy_math::mat3`, `reflection_matrix`. This generates the matrix that reflects objects across a plane, suitable for encoding into a `Transform`. It's fully documented for ease of use. Finally, a new example, `mirror`, has been added. This example is a complete instance of a working mirror, combining a camera with a Householder matrix, oblique projection, and inverted culling with a custom material to render an animated mesh and its planar reflection. The camera and mesh may be moved with the mouse, and the off-screen render target that stores the rendered contents of the mirror world is properly resized when the user resizes the window. [Lengyel oblique clipping]: https://terathon.com/lengyel/Lengyel-Oblique.pdf [Householder matrix]: https://en.wikipedia.org/wiki/Householder_transformation <img width="2564" height="1500" alt="Screenshot 2025-12-05 212155" src="https://github.com/user-attachments/assets/35652b58-a9a5-415a-bdff-367889a23b9f" /> |
||
|
|
185712fbef |
Add support for normal maps, metallic-roughness maps, and emissive maps to clustered decals. (#22039)
This commit expands the number of textures associated with each clustered decal from 1 to 4. The additional 3 textures apply normal maps, metallic-roughness maps, and emissive maps respectively to the surfaces onto which decals are projected. Normal maps are combined using the [*Whiteout* blending method] from SIGGRAPH 2007. This approach was chosen because, subjectively, it appeared better than the more complex [*reoriented normal mapping* (RNM)] approach. Additionally, *Whiteout* normal map blending is commutative and associative, unlike RNM, which is a useful property for our decals, which are currently applied in an unspecified order. (The fact that the order in which our decals are applied is unspecified is unfortunate, but is a long-standing issue and should probably be fixed in a followup.) In particular, commutativity is desirable because otherwise one must specify which normal map is the *base* normal map and which normal map is the *detail* normal map, but that's not a policy decision that Bevy can unconditionally make, as decals aren't necessary more detailed than the base normal map. (For instance, consider a bullet hole decal embedded in a wall with a subtle rough texture; one might reasonably argue that the base material's normal map is the detail map and the bullet hole is the base map, even though the bullet hole's normal map comes from a decal.) Note that, with a custom material shader, it's possible for application code to use the decal images for arbitrary other purposes. For example, with a custom shader an application might use the metallic-roughness map as a clearcoat map instead if it has no need for a metallic-roughness map on a decal. And, of course, a custom material shader could adopt RNM blending for decals if it wishes. A new example, `clustered_decal_maps`, has been added. This example demonstrates the new maps by spawning clustered decals with maps randomly over time and projecting them onto a wall. <img width="2564" height="1500" alt="Screenshot 2025-12-05 095953" src="https://github.com/user-attachments/assets/255fca64-2b42-4794-a367-14336d023310" /> |
||
|
|
0410482c13 |
Remove unused num_workgroups from game_of_life shader (#21944)
# Objective Remove unused `num_workgroups`. ## Testing ``` cargo run --example compute_shader_game_of_life ``` |
||
|
|
e8099c0ec1 |
Add support for OpenType features in text (e.g. ligatures, smallcaps) (#19020)
# Objective OpenType features include things like smallcaps, lined vs old-style numbers, ligatures, stylistic alternate characters, fractional numbers (numerator placed above the denominator), forced monospacing for numbers, and more. There are >100 possible OpenType feature tags; see https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist for the up-to-date list. This provides a way for Bevy users to use these features when using .otf fonts that support them. ## Solution OpenType features are now supported in cosmic-text, so this just provides a way to pass them through. A few notes: - I extended the existing "text" example to showcase a few different OpenType features. - OpenType features are only available for .otf fonts. Since there weren't any existing .otf fonts in the asset/ folder, I've added an SIL-licenced font so that we can showcase this in example code. - I added a "FontFeatures" struct. cosmic-text does already include its own FontFeatures struct, but 1) it does not implement Reflect, which is required by TextFont, and 2) the one I added has a couple ergonomics improvements for the builder methods compared to cosmic-text's. - OpenType font features are four characters strings, e.g. "liga". I considered representing these within an enum, but decided against this since there are hundreds of possible features, and more get added frequently, so this would require quite a bit of ongoing maintenance. Since these features are typically referred to by their four-letter name in documentation, I think the [u8; 4] representation is appropriate, and this mirrors what cosmic-text does as well. I added some consts for commonly used features. ## Testing I extended the "text" example. Run: `cargo run --example text` --- ## Showcase Screenshot:  --------- Co-authored-by: John Hansler <john@hansler.net> |
||
|
|
e3814a944b |
Atmosphere occlusion and PBR shading (#21383)
# Objective - Occlude the directional lights by the atmosphere - More Physically accurate light values reaching the objects from directional lights - Support volumetric shadowing through the atmosphere with an additional layer of FogVolume - Strategic direction: use the FogVolume and expand on that implementation for shadow sampling Since it already uses pipeline specialization. - For now keep the atmosphere pipeline as is, free from shadow sampling code since these effects only matter at large scales. Shadow sampling for the atmosphere can be implemented in a later PR for planetary rings, objects in space, other large scale objects. ## Solution - Bind the transmittance LUT to the core 3d mesh PBR shader - tint the incoming light L to point P by the transmittance through the atmospheric medium - Apply the same effects to volumetric fog - Ensured that new new Atmosphere pipeline key works with deferred rendering - Updated example to include water plane and screen space reflections. Removed the water plane from the GLB terrain asset. (small asset churn!) ## Testing ```bash cargo run --example atmosphere # or cargo run --example atmosphere --features=free_camera ``` --- ## Showcase <img width="1275" height="721" alt="Screenshot 2025-10-22 at 9 50 27 PM" src="https://github.com/user-attachments/assets/f91ec8ba-a4ee-4bd2-b205-5158193466b9" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: atlv <email@atlasdostal.com> |
||
|
|
89f9dcb431 |
Don't require cameras to have color render targets. (#20830)
It can occasionally be useful to have cameras that *only* render prepasses such as depth. Other game engines such as Unity support this feature by allowing a depth-only render target to be assigned to a camera. Bevy, however, has no easy mechanism for this. (Creating an `ShadowView` in the render app doesn't work, because various places in rendering assume that shadow views are associated with lights.) This patch fixes the problem by introducing a new type of `RenderTarget`, `RenderTarget::None`. Cameras with no render target will skip the main opaque and transparent render passes, but any prepasses on such cameras will still occur. Adding a `DepthPrepass` to such a camera enables depth-only cameras, with maximum efficiency as the fragment shader won't exist and no color buffer will be bound. Note that, when no render target is specified, the physical size of the viewport must be explicitly specified, as Bevy has no other mechanism to determine it. A new example, `render_depth_to_texture`, has been added, containing a rotating cube and a depth-only camera orbiting it. The depth texture that the camera produces is rendered onto a plane using a custom shader. (NB: In such scenarios, the depth texture must be copied from the camera to a custom image due to (a) the `wgpu` limitation that a depth texture can't be both a render target and bindable as a texture and (b) the fact that Bevy depth textures are managed by Bevy itself and exposed only to the render world. The example uses a custom render node to perform the copy.) The depth-only camera can be moved using the WASD keys. <img width="2564" height="1500" alt="Screenshot 2025-09-02 080508" src="https://github.com/user-attachments/assets/415e7f4d-393d-4be3-b569-829c06901078" /> |
||
|
|
b6922f98d1 |
Revert bevy_sprite_render rename in shaders (#20644)
Fixes #20643 |
||
|
|
3560b112f4 |
Fix imports in some 2d examples with custom shaders (#20639)
# Objective Fixes #20615 ## Solution These shaders weren't updated when the import moved in #20587. Fix the imports. ## Testing ``` cargo run --example custom_gltf_vertex_attribute cargo run --example shader_material_2d cargo run --example mesh2d_manual ``` Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
5bc5a1325a |
Update Game of Life compute example to include a uniform buffer variable (#20466)
# Objective
It is currently a little unclear how to use uniform buffers in compute
shaders. The other examples of uniform buffers in the Bevy examples and
codebase either are built on Materials or use `DynamicUniformBuffer`s
created from a `ViewNode`. Neither of these are a great fit for use in a
compute shader.
## Solution
Update the compute shader example to pass a uniform buffer to the shader
that determines the color for alive cells.
## Discussion Topics
- Is this the right way to pass this data to the shader?
- Should we be encouraging use of uniform buffers in compute shaders at
all? Some in the community prefer the ergonomics of storage buffers in
most (all?) compute shader cases. Do we want to push users to use
storage buffers instead?
- I took the idea to use color as the input from IceSentry on Discord,
but this did require me to change the texture format to support non-red
colors. Does this undermine the goals of the shader example? Is this the
wrong texture format?
## Testing
- Did you test these changes? If so, how?
- The changes were manually validated with a number of different
`LinearRgba` values for `alive_color`
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- ` cargo run --example compute_shader_game_of_life`
- Color can be set using `alive_color` property on `GameOfLifeUniforms`
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- Manually validated on Windows and WASM (WebGPU) targets
- WASM WebGL2 doesn't appear to support textures in compute shaders
---
## Showcase
<img width="1602" height="939" alt="image"
src="https://github.com/user-attachments/assets/9a535617-a179-4f20-b686-596899f11d18"
/>
---------
Co-authored-by: dontgetfoundout <inflatedego@gmail.com>
|
||
|
|
e6ec2c181d |
Material bind group shader def (#20069)
Use a shader def for the material bind group index to make it easier for when we want to switch back to group 2 in the future without breaking everyone again. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: atlv <email@atlasdostal.com> Co-authored-by: atlas dostal <rodol@rivalrebels.com> |
||
|
|
ca25a67d0d |
Fix the extended_material example on WebGL2 (#18812)
# Objective - Fixes #13872 (also mentioned in #17167) ## Solution - Added conditional padding fields to the shader uniform ## Alternatives ### 1- Use a UVec4 Replace the `u32` field in `MyExtension` by a `UVec4` and only use the `x` coordinate. (This was the original approach, but for consistency with the rest of the codebase, separate padding fields seem to be preferred) ### 2- Don't fix it, unlist it While the fix is quite simple, it does muddy the waters a tiny bit due to `quantize_steps` now being a UVec4 instead of a simple u32. We could simply remove this example from the examples that support WebGL2. ## Testing - Ran the example locally on WebGL2 (and native Vulkan) successfully |
||
|
|
831073105f |
Add comment to custom vertex attribute example to make it easier to convert to 2D (#18603)
# Objective - It's not clear what changes are needed to the shader to convert the example to 2D. - If you leave the shader unchanged you get a very confusing error (see linked issue). - Fixes #14077 ## Solution A separate example probably isn't needed as there is little difference between 3D and 2D, but a note saying what changes are needed to the shader would make it a lot easier. Let me know if you think it is also worth adding some notes to the rust file, but it is mostly trivial changes such as changing `Mesh3d` to `Mesh2d`. I have left the original code in comments next to the changes in the gist linked at the bottom if you wish to compare. ## Testing - I just spent a long time working it out the hard way. This would have made it a lot quicker. - I have tested the 2D version of the shader with the changes explained in the suggested comment and it works as expected. - For testing purposes [here is a complete working 2D example](https://gist.github.com/nickyfahey/647e2a2c45e695f24e288432b811dfc2). (note that as per the original example the shader file needs to go in 'assets/shaders/') |
||
|
|
2ea8f779c3 |
Prevent AnimationGraph from serializing AssetIds. (#19615)
# Objective - A step towards #19024. - `AnimationGraph` can serialize raw `AssetId`s. However for normal handles, this is a runtime ID. This means it is unlikely that the `AssetId` will correspond to the same asset after deserializing - effectively breaking the graph. ## Solution - Stop allowing `AssetId` to be serialized by `AnimationGraph`. Serializing a handle with no path is now an error. - Add `MigrationSerializedAnimationClip`. This is an untagged enum for serde, meaning that it will take the first variant that deserializes. So it will first try the "modern" version, then it will fallback to the legacy version. - Add some logging/error messages to explain what users should do. Note: one limitation here is that this removes the ability to serialize and deserialize UUIDs. In theory, someone could be using this to have a "default" animation. If someone inserts an empty `AnimationClip` into the `Handle::default()`, this **might** produce a T-pose. It might also do nothing though. Unclear! I think this is worth the risk for simplicity as it seems unlikely that people are sticking UUIDs in here (or that you want a default animation in **any** AnimationGraph). ## Testing - Ran `cargo r --example animation_graph -- --save` on main, then ran `cargo r --example animation_graph` on this PR. The PR was able to load the old data (after #19631). |
||
|
|
a2992fcffd |
Light Textures (#18031)
# Objective add support for light textures (also known as light cookies, light functions, and light projectors)  ## Solution - add components: ```rs /// Add to a [`PointLight`] to add a light texture effect. /// A texture mask is applied to the light source to modulate its intensity, /// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs. pub struct PointLightTexture { /// The texture image. Only the R channel is read. pub image: Handle<Image>, /// The cubemap layout. The image should be a packed cubemap in one of the formats described by the [`CubemapLayout`] enum. pub cubemap_layout: CubemapLayout, } /// Add to a [`SpotLight`] to add a light texture effect. /// A texture mask is applied to the light source to modulate its intensity, /// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs. pub struct SpotLightTexture { /// The texture image. Only the R channel is read. /// Note the border of the image should be entirely black to avoid leaking light. pub image: Handle<Image>, } /// Add to a [`DirectionalLight`] to add a light texture effect. /// A texture mask is applied to the light source to modulate its intensity, /// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs. pub struct DirectionalLightTexture { /// The texture image. Only the R channel is read. pub image: Handle<Image>, /// Whether to tile the image infinitely, or use only a single tile centered at the light's translation pub tiled: bool, } ``` - store images to the `RenderClusteredDecals` buffer - read the image and modulate the lights - add `light_textures` example to showcase the new features ## Testing see light_textures example |
||
|
|
e6ba9a6d18 |
Type erased materials (#19667)
# Objective Closes #18075 In order to enable a number of patterns for dynamic materials in the engine, it's necessary to decouple the renderer from the `Material` trait. This opens the possibility for: - Materials that aren't coupled to `AsBindGroup`. - 2d using the underlying 3d bindless infrastructure. - Dynamic materials that can change their layout at runtime. - Materials that aren't even backed by a Rust struct at all. ## Solution In short, remove all trait bounds from render world material systems and resources. This means moving a bunch of stuff onto `MaterialProperties` and engaging in some hacks to make specialization work. Rather than storing the bind group data in `MaterialBindGroupAllocator`, right now we're storing it in a closure on `MaterialProperties`. TBD if this has bad performance characteristics. ## Benchmarks - `many_cubes`: `cargo run --example many_cubes --release --features=bevy/trace_tracy -- --vary-material-data-per-instance`:  - @DGriffin91's Caldera `cargo run --release --features=bevy/trace_tracy -- --random-materials`  - @DGriffin91's Caldera with 20 unique material types (i.e. `MaterialPlugin<M>`) and random materials per mesh `cargo run --release --features=bevy/trace_tracy -- --random-materials`  ### TODO - We almost certainly lost some parallelization from removing the type params that could be gained back from smarter iteration. - Test all the things that could have broken. - ~Fix meshlets~ ## Showcase See [the example](https://github.com/bevyengine/bevy/pull/19667/files#diff-9d768cfe1c3aa81eff365d250d3cbe5a63e8df63e81dd85f64c3c3cd993f6d94) for a custom material implemented without the use of the `Material` trait and thus `AsBindGroup`.  --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> Co-authored-by: IceSentry <c.giguere42@gmail.com> |
||
|
|
96dcbc5f8c |
Ugrade to wgpu version 25.0 (#19563)
# Objective Upgrade to `wgpu` version `25.0`. Depends on https://github.com/bevyengine/naga_oil/pull/121 ## Solution ### Problem The biggest issue we face upgrading is the following requirement: > To facilitate this change, there was an additional validation rule put in place: if there is a binding array in a bind group, you may not use dynamic offset buffers or uniform buffers in that bind group. This requirement comes from vulkan rules on UpdateAfterBind descriptors. This is a major difficulty for us, as there are a number of binding arrays that are used in the view bind group. Note, this requirement does not affect merely uniform buffors that use dynamic offset but the use of *any* uniform in a bind group that also has a binding array. ### Attempted fixes The easiest fix would be to change uniforms to be storage buffers whenever binding arrays are in use: ```wgsl #ifdef BINDING_ARRAYS_ARE_USED @group(0) @binding(0) var<uniform> view: View; @group(0) @binding(1) var<uniform> lights: types::Lights; #else @group(0) @binding(0) var<storage> view: array<View>; @group(0) @binding(1) var<storage> lights: array<types::Lights>; #endif ``` This requires passing the view index to the shader so that we know where to index into the buffer: ```wgsl struct PushConstants { view_index: u32, } var<push_constant> push_constants: PushConstants; ``` Using push constants is no problem because binding arrays are only usable on native anyway. However, this greatly complicates the ability to access `view` in shaders. For example: ```wgsl #ifdef BINDING_ARRAYS_ARE_USED mesh_view_bindings::view.view_from_world[0].z #else mesh_view_bindings::view[mesh_view_bindings::view_index].view_from_world[0].z #endif ``` Using this approach would work but would have the effect of polluting our shaders with ifdef spam basically *everywhere*. Why not use a function? Unfortunately, the following is not valid wgsl as it returns a binding directly from a function in the uniform path. ```wgsl fn get_view() -> View { #if BINDING_ARRAYS_ARE_USED let view_index = push_constants.view_index; let view = views[view_index]; #endif return view; } ``` This also poses problems for things like lights where we want to return a ptr to the light data. Returning ptrs from wgsl functions isn't allowed even if both bindings were buffers. The next attempt was to simply use indexed buffers everywhere, in both the binding array and non binding array path. This would be viable if push constants were available everywhere to pass the view index, but unfortunately they are not available on webgpu. This means either passing the view index in a storage buffer (not ideal for such a small amount of state) or using push constants sometimes and uniform buffers only on webgpu. However, this kind of conditional layout infects absolutely everything. Even if we were to accept just using storage buffer for the view index, there's also the additional problem that some dynamic offsets aren't actually per-view but per-use of a setting on a camera, which would require passing that uniform data on *every* camera regardless of whether that rendering feature is being used, which is also gross. As such, although it's gross, the simplest solution just to bump binding arrays into `@group(1)` and all other bindings up one bind group. This should still bring us under the device limit of 4 for most users. ### Next steps / looking towards the future I'd like to avoid needing split our view bind group into multiple parts. In the future, if `wgpu` were to add `@builtin(draw_index)`, we could build a list of draw state in gpu processing and avoid the need for any kind of state change at all (see https://github.com/gfx-rs/wgpu/issues/6823). This would also provide significantly more flexibility to handle things like offsets into other arrays that may not be per-view. ### Testing Tested a number of examples, there are probably more that are still broken. --------- Co-authored-by: François Mockers <mockersf@gmail.com> Co-authored-by: Elabajaba <Elabajaba@users.noreply.github.com> |
||
|
|
bab31e3777 |
Initial raytraced lighting progress (bevy_solari) (#19058)
# Bevy Solari <img src="https://github.com/user-attachments/assets/94061fc8-01cf-4208-b72a-8eecad610d76" width="100" /> ## Preface - See release notes. - Please talk to me in #rendering-dev on discord or open a github discussion if you have questions about the long term plan, and keep discussion in this PR limited to the contents of the PR :) ## Connections - Works towards #639, #16408. - Spawned https://github.com/bevyengine/bevy/issues/18993. - Need to fix RT stuff in naga_oil first https://github.com/bevyengine/naga_oil/pull/116. ## This PR After nearly two years, I've revived the raytraced lighting effort I first started in https://github.com/bevyengine/bevy/pull/10000. Unlike that PR, which has realtime techniques, I've limited this PR to: * `RaytracingScenePlugin` - BLAS and TLAS building, geometry and texture binding, sampling functions. * `PathtracingPlugin` - A non-realtime path tracer intended to serve as a testbed and reference. ## What's implemented?  * BLAS building on mesh load * Emissive lights * Directional lights with soft shadows * Diffuse (lambert, not Bevy's diffuse BRDF) and emissive materials * A reference path tracer with: * Antialiasing * Direct light sampling (next event estimation) with 0/1 MIS weights * Importance-sampled BRDF bounces * Russian roulette ## What's _not_ implemented? * Anything realtime, including a real-time denoiser * Integration with Bevy's rasterized gbuffer * Specular materials * Non-opaque geometry * Any sort of CPU or GPU optimizations * BLAS compaction, proper bindless, and further RT APIs are things that we need wgpu to add * PointLights, SpotLights, or skyboxes / environment lighting * Support for materials other than StandardMaterial (and only a subset of properties are supported) * Skinned/morphed or otherwise animating/deformed meshes * Mipmaps * Adaptive self-intersection ray bias * A good way for developers to detect whether the user's GPU supports RT or not, and fallback to baked lighting. * Documentation and actual finalized APIs (literally everything is subject to change) ## End-user Usage * Have a GPU that supports RT with inline ray queries * Add `SolariPlugin` to your app * Ensure any `Mesh` asset you want to use for raytracing has `enable_raytracing: true` (defaults to true), and that it uses the standard uncompressed position/normal/uv_0/tangent vertex attribute set, triangle list topology, and 32-bit indices. * If you don't want to build a BLAS and use the mesh for RT, set enable_raytracing to false. * Add the `RaytracingMesh3d` component to your entity (separate from `Mesh3d` or `MeshletMesh3d`). ## Testing - Did you test these changes? If so, how? - Ran the solari example. - Are there any parts that need more testing? - Other test scenes probably. Normal mapping would be good to test. - How can other people (reviewers) test your changes? Is there anything specific they need to know? - See the solari.rs example for how to setup raytracing. - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? - Windows 11, NVIDIA RTX 3080. --------- Co-authored-by: atlv <email@atlasdostal.com> Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
923c2ad281 |
New cooldown example (#19234)
# Objective We want to extend our examples with a new category "usage" to demonstrate common use cases (see bevyengine/bevy-website#2131). This PR adds an example of animated cooldowns on button clicks. ## Solution - New example in "usage" directory - Implement a cooldown with an animated child Node ## Testing - I ran this on Linux - [x] test web (with bevy CLI: `bevy run --example cooldown web --open`) --------- Co-authored-by: Thierry Berger <contact@thierryberger.com> Co-authored-by: Ida "Iyes" <40234599+inodentry@users.noreply.github.com> |
||
|
|
0b4858726c |
Make entity::index non max (#18704)
# Objective There are two problems this aims to solve. First, `Entity::index` is currently a `u32`. That means there are `u32::MAX + 1` possible entities. Not only is that awkward, but it also make `Entity` allocation more difficult. I discovered this while working on remote entity reservation, but even on main, `Entities` doesn't handle the `u32::MAX + 1` entity very well. It can not be batch reserved because that iterator uses exclusive ranges, which has a maximum upper bound of `u32::MAX - 1`. In other words, having `u32::MAX` as a valid index can be thought of as a bug right now. We either need to make that invalid (this PR), which makes Entity allocation cleaner and makes remote reservation easier (because the length only needs to be u32 instead of u64, which, in atomics is a big deal), or we need to take another pass at `Entities` to make it handle the `u32::MAX` index properly. Second, `TableRow`, `ArchetypeRow` and `EntityIndex` (a type alias for u32) all have `u32` as the underlying type. That means using these as the index type in a `SparseSet` uses 64 bits for the sparse list because it stores `Option<IndexType>`. By using `NonMaxU32` here, we cut the memory of that list in half. To my knowledge, `EntityIndex` is the only thing that would really benefit from this niche. `TableRow` and `ArchetypeRow` I think are not stored in an `Option` in bulk. But if they ever are, this would help. Additionally this ensures `TableRow::INVALID` and `ArchetypeRow::INVALID` never conflict with an actual row, which in a nice bonus. As a related note, if we do components as entities where `ComponentId` becomes `Entity`, the the `SparseSet<ComponentId>` will see a similar memory improvement too. ## Solution Create a new type `EntityRow` that wraps `NonMaxU32`, similar to `TableRow` and `ArchetypeRow`. Change `Entity::index` to this type. ## Downsides `NonMax` is implemented as a `NonZero` with a binary inversion. That means accessing and storing the value takes one more instruction. I don't think that's a big deal, but it's worth mentioning. As a consequence, `to_bits` uses `transmute` to skip the inversion which keeps it a nop. But that also means that ordering has now flipped. In other words, higher indices are considered less than lower indices. I don't think that's a problem, but it's also worth mentioning. ## Alternatives We could keep the index as a u32 type and just document that `u32::MAX` is invalid, modifying `Entities` to ensure it never gets handed out. (But that's not enforced by the type system.) We could still take advantage of the niche here in `ComponentSparseSet`. We'd just need some unsafe manual conversions, which is probably fine, but opens up the possibility for correctness problems later. We could change `Entities` to fully support the `u32::MAX` index. (But that makes `Entities` more complex and potentially slightly slower.) ## Testing - CI - A few tests were changed because they depend on different ordering and `to_bits` values. ## Future Work - It might be worth removing the niche on `Entity::generation` since there is now a different niche. - We could move `Entity::generation` into it's own type too for clarity. - We should change `ComponentSparseSet` to take advantage of the new niche. (This PR doesn't change that yet.) - Consider removing or updating `Identifier`. This is only used for `Entity`, so it might be worth combining since `Entity` is now more unique. --------- Co-authored-by: atlv <email@atlasdostal.com> Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
|
|
119eb51f00 |
Fix game_of_life shader relying on Naga bug (#18951)
# Objective The game of life example shader relies on a Naga bug ([6397](https://github.com/gfx-rs/wgpu/issues/6397) / [4536](https://github.com/gfx-rs/wgpu/issues/4536)). In WGSL certain arithmetic operations must be explicitly parenthesized ([reference](https://www.w3.org/TR/WGSL/#operator-precedence-associativity)). Naga doesn't enforce that (and also the precedence order is [messed up](https://github.com/gfx-rs/wgpu/issues/4536#issuecomment-1780113990)). So this example may break soon. This is the only sample shader having this issue. ## Solution added parentheses ## Testing ran the example before and after the fix with `cargo run --example compute_shader_game_of_life` |
||
|
|
dc7c8f228f |
Add bindless support back to ExtendedMaterial. (#18025)
PR #17898 disabled bindless support for `ExtendedMaterial`. This commit adds it back. It also adds a new example, `extended_material_bindless`, showing how to use it. |
||
|
|
3945a6de3b |
Fix wesl in wasm and webgl2 (#18591)
# Objective - feature `shader_format_wesl` doesn't compile in Wasm - once fixed, example `shader_material_wesl` doesn't work in WebGL2 ## Solution - remove special path handling when loading shaders. this seems like a way to escape the asset folder which we don't want to allow, and can't compile on android or wasm, and can't work on iOS (filesystem is rooted there) - pad material so that it's 16 bits. I couldn't get conditional compilation to work in wesl for type declaration, it fails to parse - the shader renders the color `(0.0, 0.0, 0.0, 0.0)` when it's not a polka dot. this renders as black on WebGPU/metal/..., and white on WebGL2. change it to `(0.0, 0.0, 0.0, 1.0)` so that it's black everywhere |
||
|
|
35bf9753e8 |
Fixes for WESL on Windows (#18373)
# Objective WESL was broken on windows. ## Solution - Upgrade to `wesl_rs` 1.2. - Fix path handling on windows. - Improve example for khronos demo this week. |
||
|
|
c3ff6d4136 |
Fix non-crate typos (#18219)
# Objective Correct spelling ## Solution Fix typos, specifically ones that I found in folders other than /crates ## Testing CI --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
913eb46324 |
Reimplement bindless storage buffers. (#17994)
Support for bindless storage buffers was temporarily removed with the bindless revamp. This commit restores that support. |
||
|
|
181445c56b |
Add support for experimental WESL shader source (#17953)
# Objective WESL's pre-MVP `0.1.0` has been [released](https://docs.rs/wesl/latest/wesl/)! Add support for WESL shader source so that we can begin playing and testing WESL, as well as aiding in their development. ## Solution Adds a `ShaderSource::WESL` that can be used to load `.wesl` shaders. Right now, we don't support mixing `naga-oil`. Additionally, WESL shaders currently need to pass through the naga frontend, which the WESL team is aware isn't great for performance (they're working on compiling into naga modules). Also, since our shaders are managed using the asset system, we don't currently support using file based imports like `super` or package scoped imports. Further work will be needed to asses how we want to support this. --- ## Showcase See the `shader_material_wesl` example. Be sure to press space to activate party mode (trigger conditional compilation)! https://github.com/user-attachments/assets/ec6ad19f-b6e4-4e9d-a00f-6f09336b08a4 |
||
|
|
28441337bb |
Use global binding arrays for bindless resources. (#17898)
Currently, Bevy's implementation of bindless resources is rather unusual: every binding in an object that implements `AsBindGroup` (most commonly, a material) becomes its own separate binding array in the shader. This is inefficient for two reasons: 1. If multiple materials reference the same texture or other resource, the reference to that resource will be duplicated many times. This increases `wgpu` validation overhead. 2. It creates many unused binding array slots. This increases `wgpu` and driver overhead and makes it easier to hit limits on APIs that `wgpu` currently imposes tight resource limits on, like Metal. This PR fixes these issues by switching Bevy to use the standard approach in GPU-driven renderers, in which resources are de-duplicated and passed as global arrays, one for each type of resource. Along the way, this patch introduces per-platform resource limits and bumps them from 16 resources per binding array to 64 resources per bind group on Metal and 2048 resources per bind group on other platforms. (Note that the number of resources per *binding array* isn't the same as the number of resources per *bind group*; as it currently stands, if all the PBR features are turned on, Bevy could pack as many as 496 resources into a single slab.) The limits have been increased because `wgpu` now has universal support for partially-bound binding arrays, which mean that we no longer need to fill the binding arrays with fallback resources on Direct3D 12. The `#[bindless(LIMIT)]` declaration when deriving `AsBindGroup` can now simply be written `#[bindless]` in order to have Bevy choose a default limit size for the current platform. Custom limits are still available with the new `#[bindless(limit(LIMIT))]` syntax: e.g. `#[bindless(limit(8))]`. The material bind group allocator has been completely rewritten. Now there are two allocators: one for bindless materials and one for non-bindless materials. The new non-bindless material allocator simply maintains a 1:1 mapping from material to bind group. The new bindless material allocator maintains a list of slabs and allocates materials into slabs on a first-fit basis. This unfortunately makes its performance O(number of resources per object * number of slabs), but the number of slabs is likely to be low, and it's planned to become even lower in the future with `wgpu` improvements. Resources are de-duplicated with in a slab and reference counted. So, for instance, if multiple materials refer to the same texture, that texture will exist only once in the appropriate binding array. To support these new features, this patch adds the concept of a *bindless descriptor* to the `AsBindGroup` trait. The bindless descriptor allows the material bind group allocator to probe the layout of the material, now that an array of `BindGroupLayoutEntry` records is insufficient to describe the group. The `#[derive(AsBindGroup)]` has been heavily modified to support the new features. The most important user-facing change to that macro is that the struct-level `uniform` attribute, `#[uniform(BINDING_NUMBER, StandardMaterial)]`, now reads `#[uniform(BINDLESS_INDEX, MATERIAL_UNIFORM_TYPE, binding_array(BINDING_NUMBER)]`, allowing the material to specify the binding number for the binding array that holds the uniform data. To make this patch simpler, I removed support for bindless `ExtendedMaterial`s, as well as field-level bindless uniform and storage buffers. I intend to add back support for these as a follow-up. Because they aren't in any released Bevy version yet, I figured this was OK. Finally, this patch updates `StandardMaterial` for the new bindless changes. Generally, code throughout the PBR shaders that looked like `base_color_texture[slot]` now looks like `bindless_2d_textures[material_indices[slot].base_color_texture]`. This patch fixes a system hang that I experienced on the [Caldera test] when running with `caldera --random-materials --texture-count 100`. The time per frame is around 19.75 ms, down from 154.2 ms in Bevy 0.14: a 7.8× speedup. [Caldera test]: https://github.com/DGriffin91/bevy_caldera_scene |
||
|
|
02985c3d56 |
ui_material example webgl2 fix (#17852)
# Objective Fixes #17851 ## Solution Align the `slider` uniform to 16 bytes by making it a `vec4`. ## Testing Run the example using: ``` cargo run -p build-wasm-example -- --api webgl2 ui_material basic-http-server examples/wasm/ ``` |
||
|
|
fe7a29e12b |
Fix error in scene example (#17799)
# Objective After #16894, this example started logging errors: ``` ERROR bevy_asset::server: Failed to load asset 'scenes/load_scene_example.scn.ron' with asset loader 'bevy_scene::scene_loader::SceneLoader': Could not parse RON: 10:33: Expected string ``` Fixes #17798, this is the only actionable/unreported issue in there as far as I can tell. ## Solution Update the serialized scene with the expected format for `Name` ## Testing `cargo run --example scene` ## Discussion This example breaks very often and we don't always catch it. It might be nice to have this scene either 1. produce visual output so that it can be checked 2. panic if the scene fails to load (check for LoadState::Failed) Either of those would make the failures visible in [the example report](https://thebevyflock.github.io/bevy-example-runner/). Not sure which method would best suit the example. |
||
|
|
0cb3eaef67 |
Fix validation errors in Fox.glb (#17801)
# Objective Fix gltf validation errors in `Fox.glb`. Inspired by #8099, but that issue doesn't appear to describe a real bug to fix, as far as I can tell. ## Solution Use the latest version of the Fox from [glTF-Sample-Assets](https://github.com/KhronosGroup/glTF-Sample-Assets/blob/main/Models/Fox/glTF-Binary/Fox.glb). ## Testing Dropped both versions in https://github.khronos.org/glTF-Validator/ `cargo run --example animated_mesh` seems to still look fine. Before: ``` The asset contains errors. "numErrors": 126, "numWarnings": 4184, ``` After: ``` The asset is valid. "numErrors": 0, "numWarnings": 0, ``` ## Discussion The 3d testbed was panicking with ``` thread 'main' panicked at examples/testbed/3d.rs:288:60: called `Result::unwrap()` on an `Err` value: QueryDoesNotMatch(35v1 with components Transform, GlobalTransform, Visibility, InheritedVisibility, ViewVisibility, ChildOf, Children, Name) ``` Which is bizarre. I think this might be related to #17720, or maybe the structure of the gltf changed. I fixed it by using updating the testbed to use a more robust method of finding the correct entity as is done in `animated_mesh`. |
||
|
|
a861452d68 |
Add user supplied mesh tag (#17648)
# Objective Because of mesh preprocessing, users cannot rely on `@builtin(instance_index)` in order to reference external data, as the instance index is not stable, either from frame to frame or relative to the total spawn order of mesh instances. ## Solution Add a user supplied mesh index that can be used for referencing external data when drawing instanced meshes. Closes #13373 ## Testing Benchmarked `many_cubes` showing no difference in total frame time. ## Showcase https://github.com/user-attachments/assets/80620147-aafc-4d9d-a8ee-e2149f7c8f3b --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
|
|
4ecbe001d5 |
Add a custom render phase example (#16916)
# Objective - It's currently very hard for beginners and advanced users to get a full understanding of a complete render phase. ## Solution - Implement a full custom render phase - The render phase in the example is intended to show a custom stencil phase that renders the stencil in red directly on the screen --- ## Showcase <img width="1277" alt="image" src="https://github.com/user-attachments/assets/e9dc0105-4fb6-463f-ad53-0529b575fd28" /> ## Notes More docs to explain what is going on is still needed but the example works and can already help some people. We might want to consider using a batched phase and cold specialization in the future, but the example is already complex enough as it is. --------- Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com> |