Merge branch 'main' into avoid-vec-push

This commit is contained in:
Benjamin Brienen 2024-11-10 01:39:53 +01:00
commit 930fa50c1e
92 changed files with 291 additions and 154 deletions

View file

@ -2,7 +2,7 @@
name: Performance Regression
about: Bevy running slowly after upgrading? Report a performance regression.
title: ''
labels: C-Bug, C-Performance, C-Regression, S-Needs-Triage
labels: C-Bug, C-Performance, P-Regression, S-Needs-Triage
assignees: ''
---

View file

@ -244,7 +244,7 @@ jobs:
- name: First Wasm build
run: |
cargo build --release --example ui --target wasm32-unknown-unknown
cargo build --release --example testbed_ui --target wasm32-unknown-unknown
- name: Run examples
shell: bash

View file

@ -474,6 +474,7 @@ hyper = { version = "1", features = ["server", "http1"] }
http-body-util = "0.1"
anyhow = "1"
macro_rules_attribute = "0.2"
accesskit = "0.17"
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
smol = "2"
@ -3136,17 +3137,6 @@ description = "Demonstrates how to control the relative depth (z-position) of UI
category = "UI (User Interface)"
wasm = true
[[example]]
name = "ui"
path = "examples/ui/ui.rs"
doc-scrape-examples = true
[package.metadata.example.ui]
name = "UI"
description = "Illustrates various features of Bevy UI"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "ui_scaling"
path = "examples/ui/ui_scaling.rs"
@ -3857,6 +3847,13 @@ doc-scrape-examples = true
[package.metadata.example.testbed_3d]
hidden = true
[[example]]
name = "testbed_ui"
path = "examples/testbed/ui.rs"
doc-scrape-examples = true
[package.metadata.example.testbed_ui]
hidden = true
[[example]]
name = "testbed_ui_layout_rounding"

View file

@ -6,13 +6,18 @@
)]
//! Accessibility for Bevy
//!
//! As of Bevy version 0.15 `accesskit` is no longer re-exported from this crate.
//!
//! If you need to use `accesskit`, you will need to add it as a separate dependency in your `Cargo.toml`.
//!
//! Make sure to use the same version of `accesskit` as Bevy.
extern crate alloc;
use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
pub use accesskit;
use accesskit::Node;
use bevy_app::Plugin;
use bevy_derive::{Deref, DerefMut};

View file

@ -746,7 +746,15 @@ impl WeightsCurveEvaluator {
None => {
self.blend_register_blend_weight = Some(weight_to_blend);
self.blend_register_morph_target_weights.clear();
self.blend_register_morph_target_weights.extend(stack_iter);
// In the additive case, the values pushed onto the blend register need
// to be scaled by the weight.
if additive {
self.blend_register_morph_target_weights
.extend(stack_iter.map(|m| m * weight_to_blend));
} else {
self.blend_register_morph_target_weights.extend(stack_iter);
}
}
Some(ref mut current_weight) => {
@ -877,7 +885,9 @@ where
} = self.stack.pop().unwrap();
match self.blend_register.take() {
None => self.blend_register = Some((value_to_blend, weight_to_blend)),
None => {
self.initialize_blend_register(value_to_blend, weight_to_blend, additive);
}
Some((mut current_value, mut current_weight)) => {
current_weight += weight_to_blend;
@ -912,6 +922,22 @@ where
Ok(())
}
fn initialize_blend_register(&mut self, value: A, weight: f32, additive: bool) {
if additive {
let scaled_value = A::blend(
[BlendInput {
weight,
value,
additive: true,
}]
.into_iter(),
);
self.blend_register = Some((scaled_value, weight));
} else {
self.blend_register = Some((value, weight));
}
}
fn push_blend_register(
&mut self,
weight: f32,

View file

@ -217,9 +217,8 @@ pub enum AnimationNodeType {
/// additively.
///
/// The weights of all the children of this node are *not* normalized to
/// 1.0. Rather, the first child is used as a base, ignoring its weight,
/// while the others are multiplied by their respective weights and then
/// added in sequence to the base.
/// 1.0. Rather, each child is multiplied by its respective weight and
/// added in sequence.
///
/// Add nodes are primarily useful for superimposing an animation for a
/// portion of a rig on top of the main animation. For example, an add node

0
crates/bevy_animation/src/lib.rs Executable file → Normal file
View file

View file

@ -264,6 +264,17 @@ where
}
}
impl AudioPlayer<AudioSource> {
/// Creates a new [`AudioPlayer`] with the given [`Handle<AudioSource>`].
///
/// For convenience reasons, this hard-codes the [`AudioSource`] type. If you want to
/// initialize an [`AudioPlayer`] with a different type, just initialize it directly using normal
/// tuple struct syntax.
pub fn new(source: Handle<AudioSource>) -> Self {
Self(source)
}
}
/// Bundle for playing a sound.
///
/// Insert this bundle onto an entity to trigger a sound source to begin playing.

View file

@ -21,7 +21,7 @@
//!
//! fn play_background_audio(asset_server: Res<AssetServer>, mut commands: Commands) {
//! commands.spawn((
//! AudioPlayer::<AudioSource>(asset_server.load("background_audio.ogg")),
//! AudioPlayer::new(asset_server.load("background_audio.ogg")),
//! PlaybackSettings::LOOP,
//! ));
//! }

View file

@ -89,6 +89,7 @@ impl SpecializedComputePipeline for AutoExposurePipeline {
AutoExposurePass::Average => "compute_average".into(),
},
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -98,6 +98,7 @@ impl SpecializedRenderPipeline for BlitPipeline {
..Default::default()
},
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -127,6 +127,7 @@ impl SpecializedRenderPipeline for BloomDownsamplingPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -124,6 +124,7 @@ impl SpecializedRenderPipeline for BloomUpsamplingPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -233,6 +233,7 @@ impl SpecializedRenderPipeline for CasPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -160,6 +160,7 @@ impl FromWorld for CopyDeferredLightingIdPipeline {
}),
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
});
Self {

View file

@ -806,6 +806,7 @@ impl SpecializedRenderPipeline for DepthOfFieldPipeline {
},
targets,
}),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -196,6 +196,7 @@ impl SpecializedRenderPipeline for FxaaPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -141,6 +141,7 @@ impl SpecializedRenderPipeline for MotionBlurPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -208,6 +208,7 @@ fn specialize_oit_resolve_pipeline(
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}

View file

@ -344,6 +344,7 @@ impl SpecializedRenderPipeline for PostProcessingPipeline {
depth_stencil: None,
multisample: default(),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -233,6 +233,7 @@ impl SpecializedRenderPipeline for SkyboxPipeline {
write_mask: ColorWrites::ALL,
})],
}),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -105,6 +105,7 @@ impl SpecializedRenderPipeline for SkyboxPrepassPipeline {
entry_point: "fragment".into(),
targets: prepass_target_descriptors(key.normal_prepass, true, false),
}),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -512,6 +512,7 @@ impl SpecializedRenderPipeline for SmaaEdgeDetectionPipeline {
bias: default(),
}),
multisample: MultisampleState::default(),
zero_initialize_workgroup_memory: false,
}
}
}
@ -571,6 +572,7 @@ impl SpecializedRenderPipeline for SmaaBlendingWeightCalculationPipeline {
bias: default(),
}),
multisample: MultisampleState::default(),
zero_initialize_workgroup_memory: false,
}
}
}
@ -607,6 +609,7 @@ impl SpecializedRenderPipeline for SmaaNeighborhoodBlendingPipeline {
primitive: PrimitiveState::default(),
depth_stencil: None,
multisample: MultisampleState::default(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -355,6 +355,7 @@ impl SpecializedRenderPipeline for TaaPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -307,6 +307,7 @@ impl SpecializedRenderPipeline for TonemappingPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -1961,13 +1961,13 @@ pub enum ScheduleBuildError {
#[display("System dependencies contain cycle(s).\n{_0}")]
DependencyCycle(String),
/// Tried to order a system (set) relative to a system set it belongs to.
#[display("`{0}` and `{_1}` have both `in_set` and `before`-`after` relationships (these might be transitive). This combination is unsolvable as a system cannot run before or after a set it belongs to.")]
#[display("`{_0}` and `{_1}` have both `in_set` and `before`-`after` relationships (these might be transitive). This combination is unsolvable as a system cannot run before or after a set it belongs to.")]
CrossDependency(String, String),
/// Tried to order system sets that share systems.
#[display("`{0}` and `{_1}` have a `before`-`after` relationship (which may be transitive) but share systems.")]
#[display("`{_0}` and `{_1}` have a `before`-`after` relationship (which may be transitive) but share systems.")]
SetsHaveOrderButIntersect(String, String),
/// Tried to order a system (set) relative to all instances of some system function.
#[display("Tried to order against `{0}` in a schedule that has more than one `{0}` instance. `{_0}` is a `SystemTypeSet` and cannot be used for ordering if ambiguous. Use a different set without this restriction.")]
#[display("Tried to order against `{_0}` in a schedule that has more than one `{_0}` instance. `{_0}` is a `SystemTypeSet` and cannot be used for ordering if ambiguous. Use a different set without this restriction.")]
SystemTypeSetAmbiguity(String),
/// Systems with conflicting access have indeterminate run order.
///

View file

@ -161,6 +161,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
},
label: Some("LineGizmo Pipeline 2D".into()),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}
@ -261,6 +262,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
},
label: Some("LineJointGizmo Pipeline 2D".into()),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -158,6 +158,7 @@ impl SpecializedRenderPipeline for LineGizmoPipeline {
},
label: Some("LineGizmo Pipeline".into()),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}
@ -256,6 +257,7 @@ impl SpecializedRenderPipeline for LineJointGizmoPipeline {
},
label: Some("LineJointGizmo Pipeline".into()),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -248,7 +248,7 @@ pub(crate) enum ConvertAttributeError {
"Vertex attribute {_0} has format {_1:?} but expected {_3:?} for target attribute {_2}"
)]
WrongFormat(String, VertexFormat, String, VertexFormat),
#[display("{0} in accessor {_1}")]
#[display("{_0} in accessor {_1}")]
AccessFailed(AccessFailed, usize),
#[display("Unknown vertex attribute {_0}")]
UnknownName(String),

0
crates/bevy_hierarchy/src/lib.rs Executable file → Normal file
View file

View file

@ -1,3 +1,5 @@
//! [DirectDraw Surface](https://en.wikipedia.org/wiki/DirectDraw_Surface) functionality.
#[cfg(debug_assertions)]
use bevy_utils::warn_once;
use ddsfile::{Caps2, D3DFormat, Dds, DxgiFormat};
@ -16,7 +18,8 @@ pub fn dds_buffer_to_image(
is_srgb: bool,
) -> Result<Image, TextureError> {
let mut cursor = Cursor::new(buffer);
let dds = Dds::read(&mut cursor).expect("Failed to parse DDS file");
let dds = Dds::read(&mut cursor)
.map_err(|error| TextureError::InvalidData(format!("Failed to parse DDS file: {error}")))?;
let texture_format = dds_format_to_texture_format(&dds, is_srgb)?;
if !supported_compressed_formats.supports(texture_format) {
return Err(TextureError::UnsupportedTextureFormat(format!(

View file

@ -390,6 +390,7 @@ impl SpecializedRenderPipeline for DeferredLightingLayout {
}),
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -200,6 +200,7 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass<M: Material>(
entry_point: material_fragment.entry_point,
targets: material_fragment.targets,
}),
zero_initialize_workgroup_memory: false,
};
let material_id = instance_manager.get_material_id(material_id.untyped());
@ -353,6 +354,7 @@ pub fn prepare_material_meshlet_meshes_prepass<M: Material>(
entry_point,
targets: material_fragment.targets,
}),
zero_initialize_workgroup_memory: false,
};
let material_id = instance_manager.get_material_id(material_id.untyped());

View file

@ -76,6 +76,7 @@ impl FromWorld for MeshletPipelines {
shader: MESHLET_FILL_CLUSTER_BUFFERS_SHADER_HANDLE,
shader_defs: vec!["MESHLET_FILL_CLUSTER_BUFFERS_PASS".into()],
entry_point: "fill_cluster_buffers".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -92,6 +93,7 @@ impl FromWorld for MeshletPipelines {
"MESHLET_FIRST_CULLING_PASS".into(),
],
entry_point: "cull_clusters".into(),
zero_initialize_workgroup_memory: false,
}),
cull_second: pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
@ -107,6 +109,7 @@ impl FromWorld for MeshletPipelines {
"MESHLET_SECOND_CULLING_PASS".into(),
],
entry_point: "cull_clusters".into(),
zero_initialize_workgroup_memory: false,
}),
downsample_depth_first: pipeline_cache.queue_compute_pipeline(
@ -120,6 +123,7 @@ impl FromWorld for MeshletPipelines {
shader: MESHLET_DOWNSAMPLE_DEPTH_SHADER_HANDLE,
shader_defs: vec!["MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT".into()],
entry_point: "downsample_depth_first".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -134,6 +138,7 @@ impl FromWorld for MeshletPipelines {
shader: MESHLET_DOWNSAMPLE_DEPTH_SHADER_HANDLE,
shader_defs: vec!["MESHLET_VISIBILITY_BUFFER_RASTER_PASS_OUTPUT".into()],
entry_point: "downsample_depth_second".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -148,6 +153,7 @@ impl FromWorld for MeshletPipelines {
shader: MESHLET_DOWNSAMPLE_DEPTH_SHADER_HANDLE,
shader_defs: vec![],
entry_point: "downsample_depth_first".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -162,6 +168,7 @@ impl FromWorld for MeshletPipelines {
shader: MESHLET_DOWNSAMPLE_DEPTH_SHADER_HANDLE,
shader_defs: vec![],
entry_point: "downsample_depth_second".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -182,6 +189,7 @@ impl FromWorld for MeshletPipelines {
.into(),
],
entry_point: "rasterize_cluster".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -203,6 +211,7 @@ impl FromWorld for MeshletPipelines {
.into(),
],
entry_point: "rasterize_cluster".into(),
zero_initialize_workgroup_memory: false,
},
),
@ -226,6 +235,7 @@ impl FromWorld for MeshletPipelines {
.into(),
],
entry_point: "rasterize_cluster".into(),
zero_initialize_workgroup_memory: false,
}),
visibility_buffer_hardware_raster: pipeline_cache.queue_render_pipeline(
@ -269,6 +279,7 @@ impl FromWorld for MeshletPipelines {
write_mask: ColorWrites::empty(),
})],
}),
zero_initialize_workgroup_memory: false,
},
),
@ -309,6 +320,7 @@ impl FromWorld for MeshletPipelines {
write_mask: ColorWrites::empty(),
})],
}),
zero_initialize_workgroup_memory: false,
},
),
@ -356,6 +368,7 @@ impl FromWorld for MeshletPipelines {
write_mask: ColorWrites::empty(),
})],
}),
zero_initialize_workgroup_memory: false,
}),
resolve_depth: pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
@ -381,6 +394,7 @@ impl FromWorld for MeshletPipelines {
entry_point: "resolve_depth".into(),
targets: vec![],
}),
zero_initialize_workgroup_memory: false,
}),
resolve_depth_shadow_view: pipeline_cache.queue_render_pipeline(
@ -407,6 +421,7 @@ impl FromWorld for MeshletPipelines {
entry_point: "resolve_depth".into(),
targets: vec![],
}),
zero_initialize_workgroup_memory: false,
},
),
@ -434,6 +449,7 @@ impl FromWorld for MeshletPipelines {
entry_point: "resolve_material_depth".into(),
targets: vec![],
}),
zero_initialize_workgroup_memory: false,
},
),
@ -448,6 +464,7 @@ impl FromWorld for MeshletPipelines {
shader: MESHLET_REMAP_1D_TO_2D_DISPATCH_SHADER_HANDLE,
shader_defs: vec![],
entry_point: "remap_dispatch".into(),
zero_initialize_workgroup_memory: false,
})
}),
}

View file

@ -571,6 +571,7 @@ where
},
push_constant_ranges: vec![],
label: Some("prepass_pipeline".into()),
zero_initialize_workgroup_memory: false,
};
// This is a bit risky because it's possible to change something that would

View file

@ -290,6 +290,7 @@ impl SpecializedComputePipeline for PreprocessPipeline {
shader: MESH_PREPROCESS_SHADER_HANDLE,
shader_defs,
entry_point: "main".into(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -2020,6 +2020,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
alpha_to_coverage_enabled,
},
label: Some(label),
zero_initialize_workgroup_memory: false,
})
}
}

View file

@ -448,6 +448,7 @@ impl FromWorld for SsaoPipelines {
shader: PREPROCESS_DEPTH_SHADER_HANDLE,
shader_defs: Vec::new(),
entry_point: "preprocess_depth".into(),
zero_initialize_workgroup_memory: false,
});
let spatial_denoise_pipeline =
@ -461,6 +462,7 @@ impl FromWorld for SsaoPipelines {
shader: SPATIAL_DENOISE_SHADER_HANDLE,
shader_defs: Vec::new(),
entry_point: "spatial_denoise".into(),
zero_initialize_workgroup_memory: false,
});
Self {
@ -513,6 +515,7 @@ impl SpecializedComputePipeline for SsaoPipelines {
shader: SSAO_SHADER_HANDLE,
shader_defs,
entry_point: "ssao".into(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -560,6 +560,7 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline {
primitive: default(),
depth_stencil: None,
multisample: default(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -600,6 +600,7 @@ impl SpecializedRenderPipeline for VolumetricFogPipeline {
write_mask: ColorWrites::ALL,
})],
}),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -30,8 +30,8 @@
//! ## Expressive Events
//!
//! The events in this module (see [`events`]) cannot be listened to with normal `EventReader`s.
//! Instead, they are dispatched to *ovservers* attached to specific entities. When events are generated, they
//! bubble up the entity hierarchy starting from their target, until they reach the root or bubbling is haulted
//! Instead, they are dispatched to *observers* attached to specific entities. When events are generated, they
//! bubble up the entity hierarchy starting from their target, until they reach the root or bubbling is halted
//! with a call to [`Trigger::propagate`](bevy_ecs::observer::Trigger::propagate).
//! See [`Observer`] for details.
//!
@ -73,8 +73,8 @@
//!
//! #### Input Agnostic
//!
//! Picking provides a generic Pointer abstracton, which is useful for reacting to many different
//! types of input devices. Pointers can be controlled with anything, whether its the included mouse
//! Picking provides a generic Pointer abstraction, which is useful for reacting to many different
//! types of input devices. Pointers can be controlled with anything, whether it's the included mouse
//! or touch inputs, or a custom gamepad input system you write yourself to control a virtual pointer.
//!
//! ## Robustness

View file

@ -108,6 +108,9 @@ pub struct RenderPipelineDescriptor {
pub multisample: MultisampleState,
/// The compiled fragment stage, its entry point, and the color targets.
pub fragment: Option<FragmentState>,
/// Whether to zero-initialize workgroup memory by default. If you're not sure, set this to true.
/// If this is false, reading from workgroup variables before writing to them will result in garbage values.
pub zero_initialize_workgroup_memory: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
@ -147,4 +150,7 @@ pub struct ComputePipelineDescriptor {
/// The name of the entry point in the compiled shader. There must be a
/// function with this name in the shader.
pub entry_point: Cow<'static, str>,
/// Whether to zero-initialize workgroup memory by default. If you're not sure, set this to true.
/// If this is false, reading from workgroup variables before writing to them will result in garbage values.
pub zero_initialize_workgroup_memory: bool,
}

View file

@ -669,6 +669,7 @@ impl PipelineCache {
let device = self.device.clone();
let shader_cache = self.shader_cache.clone();
let layout_cache = self.layout_cache.clone();
create_pipeline_task(
async move {
let mut shader_cache = shader_cache.lock().unwrap();
@ -731,10 +732,10 @@ impl PipelineCache {
)
});
// TODO: Expose this somehow
// TODO: Expose the rest of this somehow
let compilation_options = PipelineCompilationOptions {
constants: &std::collections::HashMap::new(),
zero_initialize_workgroup_memory: false,
constants: &default(),
zero_initialize_workgroup_memory: descriptor.zero_initialize_workgroup_memory,
};
let descriptor = RawRenderPipelineDescriptor {
@ -779,6 +780,7 @@ impl PipelineCache {
let device = self.device.clone();
let shader_cache = self.shader_cache.clone();
let layout_cache = self.layout_cache.clone();
create_pipeline_task(
async move {
let mut shader_cache = shader_cache.lock().unwrap();
@ -812,10 +814,11 @@ impl PipelineCache {
layout: layout.as_ref().map(|layout| -> &PipelineLayout { layout }),
module: &compute_module,
entry_point: Some(&descriptor.entry_point),
// TODO: Expose this somehow
// TODO: Expose the rest of this somehow
compilation_options: PipelineCompilationOptions {
constants: &std::collections::HashMap::new(),
zero_initialize_workgroup_memory: false,
constants: &default(),
zero_initialize_workgroup_memory: descriptor
.zero_initialize_workgroup_memory,
},
cache: None,
};

View file

@ -496,6 +496,7 @@ impl SpecializedRenderPipeline for ScreenshotToScreenPipeline {
})],
}),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -674,6 +674,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
alpha_to_coverage_enabled: false,
},
label: Some(label.into()),
zero_initialize_workgroup_memory: false,
})
}
}

View file

@ -323,6 +323,7 @@ impl SpecializedRenderPipeline for SpritePipeline {
},
label: Some("sprite_pipeline".into()),
push_constant_ranges: Vec::new(),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -116,6 +116,8 @@ impl Plugin for TextPlugin {
.register_type::<TextColor>()
.register_type::<TextSpan>()
.register_type::<TextBounds>()
.register_type::<TextLayout>()
.register_type::<ComputedTextBlock>()
.init_asset_loader::<FontLoader>()
.init_resource::<FontAtlasSets>()
.init_resource::<TextPipeline>()

View file

@ -28,7 +28,8 @@ impl Default for CosmicBuffer {
/// A sub-entity of a [`ComputedTextBlock`].
///
/// Returned by [`ComputedTextBlock::entities`].
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Reflect)]
#[reflect(Debug)]
pub struct TextEntity {
/// The entity.
pub entity: Entity,
@ -41,7 +42,8 @@ pub struct TextEntity {
/// See [`TextLayout`].
///
/// Automatically updated by 2d and UI text systems.
#[derive(Component, Debug, Clone)]
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component, Debug, Default)]
pub struct ComputedTextBlock {
/// Buffer for managing text layout and creating [`TextLayoutInfo`].
///
@ -49,6 +51,7 @@ pub struct ComputedTextBlock {
/// `TextLayoutInfo`. If you want to control the buffer contents manually or use the `cosmic-text`
/// editor, then you need to not use `TextLayout` and instead manually implement the conversion to
/// `TextLayoutInfo`.
#[reflect(ignore)]
pub(crate) buffer: CosmicBuffer,
/// Entities for all text spans in the block, including the root-level text.
///

View file

@ -43,6 +43,7 @@ derive_more = { version = "1", default-features = false, features = [
] }
nonmax = "0.5"
smallvec = "1.11"
accesskit = "0.17"
[features]
default = ["bevy_ui_picking_backend"]

View file

@ -1,13 +1,10 @@
use crate::{
experimental::UiChildren,
prelude::{Button, Label},
widget::{TextUiReader, UiImage},
widget::{ImageNode, TextUiReader},
ComputedNode,
};
use bevy_a11y::{
accesskit::{Node, Rect, Role},
AccessibilityNode,
};
use bevy_a11y::AccessibilityNode;
use bevy_app::{App, Plugin, PostUpdate};
use bevy_ecs::{
prelude::{DetectChanges, Entity},
@ -19,6 +16,8 @@ use bevy_ecs::{
use bevy_render::{camera::CameraUpdateSystem, prelude::Camera};
use bevy_transform::prelude::GlobalTransform;
use accesskit::{Node, Rect, Role};
fn calc_label(
text_reader: &mut TextUiReader,
children: impl Iterator<Item = Entity>,
@ -92,7 +91,10 @@ fn button_changed(
fn image_changed(
mut commands: Commands,
mut query: Query<(Entity, Option<&mut AccessibilityNode>), (Changed<UiImage>, Without<Button>)>,
mut query: Query<
(Entity, Option<&mut AccessibilityNode>),
(Changed<ImageNode>, Without<Button>),
>,
ui_children: UiChildren,
mut text_reader: TextUiReader,
) {

View file

@ -8,7 +8,7 @@
//! This crate contains Bevy's UI system, which can be used to create UI for both 2D and 3D games
//! # Basic usage
//! Spawn UI elements with [`widget::Button`], [`UiImage`], [`Text`](prelude::Text) and [`Node`]
//! Spawn UI elements with [`widget::Button`], [`ImageNode`], [`Text`](prelude::Text) and [`Node`]
//! This UI is laid out with the Flexbox and CSS Grid layout models (see <https://cssreference.io/flexbox/>)
pub mod measurement;
@ -40,7 +40,7 @@ pub use measurement::*;
pub use render::*;
pub use ui_material::*;
pub use ui_node::*;
use widget::{UiImage, UiImageSize};
use widget::{ImageNode, ImageNodeSize};
/// The UI prelude.
///
@ -58,7 +58,7 @@ pub mod prelude {
node_bundles::*,
ui_material::*,
ui_node::*,
widget::{Button, Label, UiImage},
widget::{Button, ImageNode, Label},
Interaction, MaterialNode, UiMaterialPlugin, UiScale,
},
// `bevy_sprite` re-exports for texture slicing
@ -155,8 +155,8 @@ impl Plugin for UiPlugin {
.register_type::<RelativeCursorPosition>()
.register_type::<ScrollPosition>()
.register_type::<TargetCamera>()
.register_type::<UiImage>()
.register_type::<UiImageSize>()
.register_type::<ImageNode>()
.register_type::<ImageNodeSize>()
.register_type::<UiRect>()
.register_type::<UiScale>()
.register_type::<BorderColor>()
@ -208,7 +208,7 @@ impl Plugin for UiPlugin {
update_clipping_system.after(TransformSystem::TransformPropagate),
// Potential conflicts: `Assets<Image>`
// They run independently since `widget::image_node_system` will only ever observe
// its own UiImage, and `widget::text_system` & `bevy_text::update_text2d_layout`
// its own ImageNode, and `widget::text_system` & `bevy_text::update_text2d_layout`
// will never modify a pre-existing `Image` asset.
widget::update_image_content_size_system
.in_set(UiSystem::Prepare)
@ -265,7 +265,7 @@ fn build_text_interop(app: &mut App) {
// Since both systems will only ever insert new [`Image`] assets,
// they will never observe each other's effects.
.ambiguous_with(bevy_text::update_text2d_layout)
// We assume Text is on disjoint UI entities to UiImage and UiTextureAtlasImage
// We assume Text is on disjoint UI entities to ImageNode and UiTextureAtlasImage
// FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481.
.ambiguous_with(widget::update_image_content_size_system),
widget::text_system

View file

@ -2,9 +2,9 @@
#![expect(deprecated)]
use crate::{
widget::{Button, UiImageSize},
BackgroundColor, BorderColor, BorderRadius, ComputedNode, ContentSize, FocusPolicy,
Interaction, MaterialNode, Node, ScrollPosition, UiImage, UiMaterial, ZIndex,
widget::{Button, ImageNodeSize},
BackgroundColor, BorderColor, BorderRadius, ComputedNode, ContentSize, FocusPolicy, ImageNode,
Interaction, MaterialNode, Node, ScrollPosition, UiMaterial, ZIndex,
};
use bevy_ecs::bundle::Bundle;
use bevy_render::view::{InheritedVisibility, ViewVisibility, Visibility};
@ -60,7 +60,7 @@ pub struct NodeBundle {
#[derive(Bundle, Debug, Default)]
#[deprecated(
since = "0.15.0",
note = "Use the `UiImage` component instead. Inserting `UiImage` will also insert the other components required automatically."
note = "Use the `ImageNode` component instead. Inserting `ImageNode` will also insert the other components required automatically."
)]
pub struct ImageBundle {
/// Describes the logical size of the node
@ -73,7 +73,7 @@ pub struct ImageBundle {
/// The image of the node.
///
/// To tint the image, change the `color` field of this component.
pub image: UiImage,
pub image: ImageNode,
/// The color of the background that will fill the containing node.
pub background_color: BackgroundColor,
/// The border radius of the node
@ -81,7 +81,7 @@ pub struct ImageBundle {
/// The size of the image in pixels
///
/// This component is set automatically
pub image_size: UiImageSize,
pub image_size: ImageNodeSize,
/// Whether this node should block interaction with lower nodes
pub focus_policy: FocusPolicy,
/// The transform of the node
@ -126,7 +126,7 @@ pub struct ButtonBundle {
/// The border radius of the node
pub border_radius: BorderRadius,
/// The image of the node
pub image: UiImage,
pub image: ImageNode,
/// The background color that will fill the containing node
pub background_color: BackgroundColor,
/// The transform of the node

View file

@ -206,6 +206,7 @@ impl SpecializedRenderPipeline for BoxShadowPipeline {
alpha_to_coverage_enabled: false,
},
label: Some("box_shadow_pipeline".into()),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -4,7 +4,7 @@ mod render_pass;
mod ui_material_pipeline;
pub mod ui_texture_slice_pipeline;
use crate::widget::UiImage;
use crate::widget::ImageNode;
use crate::{
experimental::UiChildren, BackgroundColor, BorderColor, CalculatedClip, ComputedNode,
DefaultUiCamera, Outline, ResolvedBorderRadius, TargetCamera, UiAntiAlias, UiBoxShadowSamples,
@ -110,7 +110,7 @@ pub fn build_ui_render(app: &mut App) {
render_app
.init_resource::<SpecializedRenderPipelines<UiPipeline>>()
.init_resource::<UiImageBindGroups>()
.init_resource::<ImageNodeBindGroups>()
.init_resource::<UiMeta>()
.init_resource::<ExtractedUiNodes>()
.allow_ambiguous_resource::<ExtractedUiNodes>()
@ -318,7 +318,7 @@ pub fn extract_uinode_images(
&ViewVisibility,
Option<&CalculatedClip>,
Option<&TargetCamera>,
&UiImage,
&ImageNode,
)>,
>,
mapping: Extract<Query<RenderEntity>>,
@ -874,7 +874,7 @@ pub fn queue_uinodes(
}
#[derive(Resource, Default)]
pub struct UiImageBindGroups {
pub struct ImageNodeBindGroups {
pub values: HashMap<AssetId<Image>, BindGroup>,
}
@ -887,7 +887,7 @@ pub fn prepare_uinodes(
mut extracted_uinodes: ResMut<ExtractedUiNodes>,
view_uniforms: Res<ViewUniforms>,
ui_pipeline: Res<UiPipeline>,
mut image_bind_groups: ResMut<UiImageBindGroups>,
mut image_bind_groups: ResMut<ImageNodeBindGroups>,
gpu_images: Res<RenderAssets<GpuImage>>,
mut phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
events: Res<SpriteAssetEvents>,

View file

@ -121,6 +121,7 @@ impl SpecializedRenderPipeline for UiPipeline {
alpha_to_coverage_enabled: false,
},
label: Some("ui_pipeline".into()),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -1,6 +1,6 @@
use core::ops::Range;
use super::{UiBatch, UiImageBindGroups, UiMeta};
use super::{ImageNodeBindGroups, UiBatch, UiMeta};
use crate::DefaultCameraView;
use bevy_ecs::{
prelude::*,
@ -185,7 +185,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
}
pub struct SetUiTextureBindGroup<const I: usize>;
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I> {
type Param = SRes<UiImageBindGroups>;
type Param = SRes<ImageNodeBindGroups>;
type ViewQuery = ();
type ItemQuery = Read<UiBatch>;

View file

@ -199,6 +199,7 @@ where
alpha_to_coverage_enabled: false,
},
label: Some("ui_material_pipeline".into()),
zero_initialize_workgroup_memory: false,
};
if let Some(vertex_shader) = &self.vertex_shader {
descriptor.vertex.shader = vertex_shader.clone();

View file

@ -30,7 +30,7 @@ use bevy_transform::prelude::GlobalTransform;
use bevy_utils::HashMap;
use binding_types::{sampler, texture_2d};
use bytemuck::{Pod, Zeroable};
use widget::UiImage;
use widget::ImageNode;
pub const UI_SLICER_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(11156288772117983964);
@ -219,6 +219,7 @@ impl SpecializedRenderPipeline for UiTextureSlicePipeline {
alpha_to_coverage_enabled: false,
},
label: Some("ui_texture_slice_pipeline".into()),
zero_initialize_workgroup_memory: false,
}
}
}
@ -256,7 +257,7 @@ pub fn extract_ui_texture_slices(
&ViewVisibility,
Option<&CalculatedClip>,
Option<&TargetCamera>,
&UiImage,
&ImageNode,
)>,
>,
mapping: Extract<Query<RenderEntity>>,

View file

@ -9,11 +9,11 @@ use bevy_sprite::{TextureAtlas, TextureAtlasLayout, TextureSlicer};
use bevy_window::{PrimaryWindow, Window};
use taffy::{MaybeMath, MaybeResolve};
/// The 2D texture displayed for this UI node
/// A UI Node that renders an image.
#[derive(Component, Clone, Debug, Reflect)]
#[reflect(Component, Default, Debug)]
#[require(Node, UiImageSize, ContentSize)]
pub struct UiImage {
#[require(Node, ImageNodeSize, ContentSize)]
pub struct ImageNode {
/// The tint color used to draw the image.
///
/// This is multiplied by the color of each pixel in the image.
@ -39,16 +39,16 @@ pub struct UiImage {
pub image_mode: NodeImageMode,
}
impl Default for UiImage {
impl Default for ImageNode {
/// A transparent 1x1 image with a solid white tint.
///
/// # Warning
///
/// This will be invisible by default.
/// To set this to a visible image, you need to set the `texture` field to a valid image handle,
/// or use [`Handle<Image>`]'s default 1x1 solid white texture (as is done in [`UiImage::solid_color`]).
/// or use [`Handle<Image>`]'s default 1x1 solid white texture (as is done in [`ImageNode::solid_color`]).
fn default() -> Self {
UiImage {
ImageNode {
// This should be white because the tint is multiplied with the image,
// so if you set an actual image with default tint you'd want its original colors
color: Color::WHITE,
@ -63,8 +63,8 @@ impl Default for UiImage {
}
}
impl UiImage {
/// Create a new [`UiImage`] with the given texture.
impl ImageNode {
/// Create a new [`ImageNode`] with the given texture.
pub fn new(texture: Handle<Image>) -> Self {
Self {
image: texture,
@ -73,7 +73,7 @@ impl UiImage {
}
}
/// Create a solid color [`UiImage`].
/// Create a solid color [`ImageNode`].
///
/// This is primarily useful for debugging / mocking the extents of your image.
pub fn solid_color(color: Color) -> Self {
@ -88,7 +88,7 @@ impl UiImage {
}
}
/// Create a [`UiImage`] from an image, with an associated texture atlas
/// Create a [`ImageNode`] from an image, with an associated texture atlas
pub fn from_atlas_image(image: Handle<Image>, atlas: TextureAtlas) -> Self {
Self {
image,
@ -131,7 +131,7 @@ impl UiImage {
}
}
impl From<Handle<Image>> for UiImage {
impl From<Handle<Image>> for ImageNode {
fn from(texture: Handle<Image>) -> Self {
Self::new(texture)
}
@ -175,14 +175,14 @@ impl NodeImageMode {
/// This component is updated automatically by [`update_image_content_size_system`]
#[derive(Component, Debug, Copy, Clone, Default, Reflect)]
#[reflect(Component, Default, Debug)]
pub struct UiImageSize {
pub struct ImageNodeSize {
/// The size of the image's texture
///
/// This field is updated automatically by [`update_image_content_size_system`]
size: UVec2,
}
impl UiImageSize {
impl ImageNodeSize {
/// The size of the image's texture
pub fn size(&self) -> UVec2 {
self.size
@ -259,7 +259,7 @@ pub fn update_image_content_size_system(
textures: Res<Assets<Image>>,
atlases: Res<Assets<TextureAtlasLayout>>,
mut query: Query<(&mut ContentSize, Ref<UiImage>, &mut UiImageSize), UpdateImageFilter>,
mut query: Query<(&mut ContentSize, Ref<ImageNode>, &mut ImageNodeSize), UpdateImageFilter>,
) {
let combined_scale_factor = windows
.get_single()

View file

@ -52,6 +52,7 @@ raw-window-handle = "0.6"
serde = { version = "1.0", features = ["derive"], optional = true }
bytemuck = { version = "1.5", optional = true }
wgpu-types = { version = "23", optional = true }
accesskit = "0.17"
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2" }

View file

@ -3,12 +3,12 @@
use alloc::{collections::VecDeque, sync::Arc};
use std::sync::Mutex;
use accesskit::{
ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Node, NodeId, Role, Tree,
TreeUpdate,
};
use accesskit_winit::Adapter;
use bevy_a11y::{
accesskit::{
ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Node, NodeId, Role,
Tree, TreeUpdate,
},
AccessibilityNode, AccessibilityRequested, AccessibilitySystem,
ActionRequest as ActionRequestWrapper, Focus, ManageAccessibilityUpdates,
};

View file

@ -385,8 +385,11 @@ pub(crate) fn changed_windows(
}
}
if window.cursor_options.grab_mode != cache.window.cursor_options.grab_mode {
crate::winit_windows::attempt_grab(winit_window, window.cursor_options.grab_mode);
if window.cursor_options.grab_mode != cache.window.cursor_options.grab_mode
&& crate::winit_windows::attempt_grab(winit_window, window.cursor_options.grab_mode)
.is_err()
{
window.cursor_options.grab_mode = cache.window.cursor_options.grab_mode;
}
if window.cursor_options.visible != cache.window.cursor_options.visible {

View file

@ -10,6 +10,7 @@ use bevy_window::{
use winit::{
dpi::{LogicalSize, PhysicalPosition},
error::ExternalError,
event_loop::ActiveEventLoop,
monitor::{MonitorHandle, VideoModeHandle},
window::{CursorGrabMode as WinitCursorGrabMode, Fullscreen, Window as WinitWindow, WindowId},
@ -279,7 +280,7 @@ impl WinitWindows {
// Do not set the grab mode on window creation if it's none. It can fail on mobile.
if window.cursor_options.grab_mode != CursorGrabMode::None {
attempt_grab(&winit_window, window.cursor_options.grab_mode);
let _ = attempt_grab(&winit_window, window.cursor_options.grab_mode);
}
winit_window.set_cursor_visible(window.cursor_options.visible);
@ -380,7 +381,10 @@ pub fn get_best_videomode(monitor: &MonitorHandle) -> VideoModeHandle {
modes.first().unwrap().clone()
}
pub(crate) fn attempt_grab(winit_window: &WinitWindow, grab_mode: CursorGrabMode) {
pub(crate) fn attempt_grab(
winit_window: &WinitWindow,
grab_mode: CursorGrabMode,
) -> Result<(), ExternalError> {
let grab_result = match grab_mode {
CursorGrabMode::None => winit_window.set_cursor_grab(WinitCursorGrabMode::None),
CursorGrabMode::Confined => winit_window
@ -398,6 +402,9 @@ pub(crate) fn attempt_grab(winit_window: &WinitWindow, grab_mode: CursorGrabMode
};
bevy_utils::tracing::error!("Unable to {} cursor: {}", err_desc, err);
Err(err)
} else {
Ok(())
}
}

View file

@ -220,6 +220,7 @@ impl SpecializedRenderPipeline for ColoredMesh2dPipeline {
alpha_to_coverage_enabled: false,
},
label: Some("colored_mesh2d_pipeline".into()),
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -115,7 +115,7 @@ fn setup(
));
commands.spawn((
UiImage {
ImageNode {
image: metering_mask,
..default()
},
@ -162,7 +162,7 @@ struct ExampleResources {
fn example_control_system(
camera: Single<(&mut Transform, &mut AutoExposure), With<Camera3d>>,
mut display: Single<&mut Text, With<ExampleDisplay>>,
mut mask_image: Single<&mut Node, With<UiImage>>,
mut mask_image: Single<&mut Node, With<ImageNode>>,
time: Res<Time>,
input: Res<ButtonInput<KeyCode>>,
resources: Res<ExampleResources>,

View file

@ -516,7 +516,6 @@ Example | Description
[Text Debug](../examples/ui/text_debug.rs) | An example for debugging text layout
[Text Wrap Debug](../examples/ui/text_wrap_debug.rs) | Demonstrates text wrapping
[Transparency UI](../examples/ui/transparency_ui.rs) | Demonstrates transparency for UI
[UI](../examples/ui/ui.rs) | Illustrates various features of Bevy UI
[UI Material](../examples/ui/ui_material.rs) | Demonstrates creating and using custom Ui materials
[UI Scaling](../examples/ui/ui_scaling.rs) | Illustrates how to scale the UI
[UI Texture Atlas](../examples/ui/ui_texture_atlas.rs) | Illustrates how to use TextureAtlases in UI

View file

@ -361,7 +361,7 @@ fn setup_animation_graph_once_loaded(
.from_asset("models/animated/Fox.glb"),
);
let mask = if animation_index == 0 { 0 } else { 0x3f };
animation_graph.add_clip_with_mask(handle, mask, 0.0, blend_node)
animation_graph.add_clip_with_mask(handle, mask, 1.0, blend_node)
});
// Create each mask group.

View file

@ -11,7 +11,7 @@ fn main() {
}
fn setup(asset_server: Res<AssetServer>, mut commands: Commands) {
commands.spawn(AudioPlayer::<AudioSource>(
commands.spawn(AudioPlayer::new(
asset_server.load("sounds/Windless Slopes.ogg"),
));
}

View file

@ -12,7 +12,7 @@ fn main() {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
MyMusic,
));
}

View file

@ -38,7 +38,7 @@ fn setup(
MeshMaterial2d(materials.add(Color::from(BLUE))),
Transform::from_translation(Vec3::new(0.0, 50.0, 0.0)),
Emitter::default(),
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
PlaybackSettings::LOOP.with_spatial(true),
));

View file

@ -29,7 +29,7 @@ fn setup(
MeshMaterial3d(materials.add(Color::from(BLUE))),
Transform::from_xyz(0.0, 0.0, 0.0),
Emitter::default(),
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
PlaybackSettings::LOOP.with_spatial(true),
));

View file

@ -6,7 +6,7 @@ use bevy::prelude::*;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
#[derive(Clone, Eq, PartialEq, Debug, Hash, Default, States)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
enum GameState {
#[default]
Playing,
@ -25,6 +25,7 @@ fn main() {
TimerMode::Repeating,
)))
.init_state::<GameState>()
.enable_state_scoped_entities::<GameState>()
.add_systems(Startup, setup_cameras)
.add_systems(OnEnter(GameState::Playing), setup)
.add_systems(
@ -38,13 +39,11 @@ fn main() {
)
.run_if(in_state(GameState::Playing)),
)
.add_systems(OnExit(GameState::Playing), teardown)
.add_systems(OnEnter(GameState::GameOver), display_score)
.add_systems(
Update,
gameover_keyboard.run_if(in_state(GameState::GameOver)),
)
.add_systems(OnExit(GameState::GameOver), teardown)
.run();
}
@ -122,6 +121,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
game.player.move_cooldown = Timer::from_seconds(0.3, TimerMode::Once);
commands.spawn((
StateScoped(GameState::Playing),
PointLight {
intensity: 2_000_000.0,
shadows_enabled: true,
@ -140,6 +140,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
.map(|i| {
let height = rng.gen_range(-0.1..0.1);
commands.spawn((
StateScoped(GameState::Playing),
Transform::from_xyz(i as f32, height - 0.2, j as f32),
SceneRoot(cell_scene.clone()),
));
@ -153,6 +154,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
game.player.entity = Some(
commands
.spawn((
StateScoped(GameState::Playing),
Transform {
translation: Vec3::new(
game.player.i as f32,
@ -176,6 +178,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
// scoreboard
commands.spawn((
StateScoped(GameState::Playing),
Text::new("Score:"),
TextFont {
font_size: 33.0,
@ -193,13 +196,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
commands.insert_resource(Random(rng));
}
// remove all entities that are not a camera or window
fn teardown(mut commands: Commands, entities: Query<Entity, (Without<Camera>, Without<Window>)>) {
for entity in &entities {
commands.entity(entity).despawn();
}
}
// control the game character
fn move_player(
mut commands: Commands,
@ -344,6 +340,7 @@ fn spawn_bonus(
game.bonus.entity = Some(
commands
.spawn((
StateScoped(GameState::Playing),
Transform::from_xyz(
game.bonus.i as f32,
game.board[game.bonus.j][game.bonus.i].height + 0.2,
@ -393,12 +390,15 @@ fn gameover_keyboard(
// display the number of cake eaten before losing
fn display_score(mut commands: Commands, game: Res<Game>) {
commands
.spawn(Node {
width: Val::Percent(100.),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
})
.spawn((
StateScoped(GameState::GameOver),
Node {
width: Val::Percent(100.),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
))
.with_child((
Text::new(format!("Cake eaten: {}", game.cake_eaten)),
TextFont {

View file

@ -86,7 +86,7 @@ mod splash {
))
.with_children(|parent| {
parent.spawn((
UiImage::new(icon),
ImageNode::new(icon),
Node {
// This will set the logo to be 200px wide, and auto adjust its height
width: Val::Px(200.0),
@ -444,7 +444,7 @@ mod menu {
))
.with_children(|parent| {
let icon = asset_server.load("textures/Game Icons/right.png");
parent.spawn((UiImage::new(icon), button_icon_node.clone()));
parent.spawn((ImageNode::new(icon), button_icon_node.clone()));
parent.spawn((
Text::new("New Game"),
button_text_font.clone(),
@ -460,7 +460,7 @@ mod menu {
))
.with_children(|parent| {
let icon = asset_server.load("textures/Game Icons/wrench.png");
parent.spawn((UiImage::new(icon), button_icon_node.clone()));
parent.spawn((ImageNode::new(icon), button_icon_node.clone()));
parent.spawn((
Text::new("Settings"),
button_text_font.clone(),
@ -476,7 +476,7 @@ mod menu {
))
.with_children(|parent| {
let icon = asset_server.load("textures/Game Icons/exitRight.png");
parent.spawn((UiImage::new(icon), button_icon_node));
parent.spawn((ImageNode::new(icon), button_icon_node));
parent.spawn((
Text::new("Quit"),
button_text_font,

View file

@ -162,7 +162,7 @@ fn button_handler(
fn setup_music(asset_server: Res<AssetServer>, mut commands: Commands) {
commands.spawn((
AudioPlayer::<AudioSource>(asset_server.load("sounds/Windless Slopes.ogg")),
AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
PlaybackSettings::LOOP,
));
}

View file

@ -179,6 +179,7 @@ impl FromWorld for GameOfLifePipeline {
shader: shader.clone(),
shader_defs: vec![],
entry_point: Cow::from("init"),
zero_initialize_workgroup_memory: false,
});
let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
label: None,
@ -187,6 +188,7 @@ impl FromWorld for GameOfLifePipeline {
shader,
shader_defs: vec![],
entry_point: Cow::from("update"),
zero_initialize_workgroup_memory: false,
});
GameOfLifePipeline {

View file

@ -343,6 +343,7 @@ impl SpecializedRenderPipeline for CustomPhasePipeline {
mask: !0,
alpha_to_coverage_enabled: false,
},
zero_initialize_workgroup_memory: false,
}
}
}

View file

@ -287,6 +287,7 @@ impl FromWorld for PostProcessPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
push_constant_ranges: vec![],
zero_initialize_workgroup_memory: false,
});
Self {

View file

@ -180,6 +180,7 @@ impl FromWorld for ComputePipeline {
shader: shader.clone(),
shader_defs: Vec::new(),
entry_point: "main".into(),
zero_initialize_workgroup_memory: false,
});
ComputePipeline { layout, pipeline }
}

View file

@ -263,6 +263,7 @@ impl SpecializedMeshPipeline for CustomMeshPipeline {
count: mesh_key.msaa_samples(),
..MultisampleState::default()
},
zero_initialize_workgroup_memory: false,
})
}
}

View file

@ -252,7 +252,7 @@ fn spawn_button(
));
if let Some(image) = image {
builder.insert(UiImage::new(image));
builder.insert(ImageNode::new(image));
}
if spawn_text {

View file

@ -2,11 +2,9 @@
use std::f32::consts::PI;
use accesskit::{Node as Accessible, Role};
use bevy::{
a11y::{
accesskit::{Node as Accessible, Role},
AccessibilityNode,
},
a11y::AccessibilityNode,
color::palettes::{basic::LIME, css::DARK_GRAY},
input::mouse::{MouseScrollUnit, MouseWheel},
picking::focus::HoverMap,
@ -182,7 +180,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
))
.with_children(|parent| {
parent.spawn((
UiImage::new(asset_server.load("branding/bevy_logo_light.png")),
ImageNode::new(asset_server.load("branding/bevy_logo_light.png")),
// Uses the transform to rotate the logo image by 45 degrees
Transform::from_rotation(Quat::from_rotation_z(0.25 * PI)),
BorderRadius::all(Val::Px(10.)),
@ -294,7 +292,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// bevy logo (image)
parent
.spawn((
UiImage::new(asset_server.load("branding/bevy_logo_dark_big.png"))
ImageNode::new(asset_server.load("branding/bevy_logo_dark_big.png"))
.with_mode(NodeImageMode::Stretch),
Node {
width: Val::Px(500.0),
@ -334,17 +332,17 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
[(false, false), (false, true), (true, true), (true, false)]
{
parent.spawn((
Node {
// The height will be chosen automatically to preserve the image's aspect ratio
width: Val::Px(75.),
..default()
},
UiImage {
ImageNode {
image: asset_server.load("branding/icon.png"),
flip_x,
flip_y,
..default()
},
Node {
// The height will be chosen automatically to preserve the image's aspect ratio
width: Val::Px(75.),
..default()
},
));
}
});

View file

@ -49,7 +49,7 @@ fn atlas_render_system(
let font_atlas = &font_atlas[state.atlas_count as usize];
state.atlas_count += 1;
commands.spawn((
UiImage::new(font_atlas.texture.clone()),
ImageNode::new(font_atlas.texture.clone()),
Node {
position_type: PositionType::Absolute,
top: Val::ZERO,

View file

@ -77,7 +77,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
))
.with_children(|parent| {
parent.spawn((
UiImage::new(image.clone()),
ImageNode::new(image.clone()),
Node {
min_width: Val::Px(100.),
min_height: Val::Px(100.),

View file

@ -80,7 +80,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
BackgroundColor(LIGHT_CYAN.into()),
))
.with_child((
UiImage::new(image.clone()),
ImageNode::new(image.clone()),
Node {
min_width: Val::Px(100.),
min_height: Val::Px(100.),

View file

@ -141,7 +141,7 @@ fn spawn_image(
) {
spawn_container(parent, update_transform, |parent| {
parent.spawn((
UiImage::new(asset_server.load("branding/bevy_logo_dark_big.png")),
ImageNode::new(asset_server.load("branding/bevy_logo_dark_big.png")),
Node {
height: Val::Px(100.),
position_type: PositionType::Absolute,

View file

@ -1,10 +1,8 @@
//! This example illustrates scrolling in Bevy UI.
use accesskit::{Node as Accessible, Role};
use bevy::{
a11y::{
accesskit::{Node as Accessible, Role},
AccessibilityNode,
},
a11y::AccessibilityNode,
input::mouse::{MouseScrollUnit, MouseWheel},
picking::focus::HoverMap,
prelude::*,

View file

@ -17,13 +17,13 @@ fn main() {
.run();
}
// A unit struct to help identify the FPS UI component, since there may be many Text components
// Marker struct to help identify the FPS UI component, since there may be many Text components
#[derive(Component)]
struct FpsText;
// A unit struct to help identify the color-changing Text component
// Marker struct to help identify the color-changing Text component
#[derive(Component)]
struct ColorText;
struct AnimatedText;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// UI camera
@ -47,7 +47,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
right: Val::Px(5.0),
..default()
},
ColorText,
AnimatedText,
));
// Text with multiple sections
@ -116,7 +116,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
));
}
fn text_color_system(time: Res<Time>, mut query: Query<&mut TextColor, With<ColorText>>) {
fn text_color_system(time: Res<Time>, mut query: Query<&mut TextColor, With<AnimatedText>>) {
for mut text_color in &mut query {
let seconds = time.elapsed_secs();

View file

@ -64,7 +64,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
BackgroundColor(BLUE.into()),
));
parent.spawn((
UiImage::new(asset_server.load("branding/icon.png")),
ImageNode::new(asset_server.load("branding/icon.png")),
Node {
width: Val::Px(30.0),
height: Val::Px(30.0),

View file

@ -44,7 +44,10 @@ fn setup(
})
.with_children(|parent| {
parent.spawn((
UiImage::from_atlas_image(texture_handle, TextureAtlas::from(texture_atlas_handle)),
ImageNode::from_atlas_image(
texture_handle,
TextureAtlas::from(texture_atlas_handle),
),
Node {
width: Val::Px(256.),
height: Val::Px(256.),
@ -64,10 +67,13 @@ fn setup(
});
}
fn increment_atlas_index(mut ui_images: Query<&mut UiImage>, keyboard: Res<ButtonInput<KeyCode>>) {
fn increment_atlas_index(
mut image_nodes: Query<&mut ImageNode>,
keyboard: Res<ButtonInput<KeyCode>>,
) {
if keyboard.just_pressed(KeyCode::Space) {
for mut ui_image in &mut ui_images {
if let Some(atlas) = &mut ui_image.texture_atlas {
for mut image_node in &mut image_nodes {
if let Some(atlas) = &mut image_node.texture_atlas {
atlas.index = (atlas.index + 1) % 6;
}
}

View file

@ -20,7 +20,7 @@ fn main() {
fn button_system(
mut interaction_query: Query<
(&Interaction, &Children, &mut UiImage),
(&Interaction, &Children, &mut ImageNode),
(Changed<Interaction>, With<Button>),
>,
mut text_query: Query<&mut Text>,
@ -82,7 +82,7 @@ fn setup(
parent
.spawn((
Button,
UiImage::from_atlas_image(
ImageNode::from_atlas_image(
texture_handle.clone(),
TextureAtlas {
index: idx,

View file

@ -20,7 +20,7 @@ fn main() {
fn button_system(
mut interaction_query: Query<
(&Interaction, &Children, &mut UiImage),
(&Interaction, &Children, &mut ImageNode),
(Changed<Interaction>, With<Button>),
>,
mut text_query: Query<&mut Text>,
@ -68,6 +68,11 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
parent
.spawn((
Button,
ImageNode {
image: image.clone(),
image_mode: NodeImageMode::Sliced(slicer.clone()),
..default()
},
Node {
width: Val::Px(w),
height: Val::Px(h),
@ -78,8 +83,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
margin: UiRect::all(Val::Px(20.0)),
..default()
},
UiImage::new(image.clone())
.with_mode(NodeImageMode::Sliced(slicer.clone())),
))
.with_child((
Text::new("Button"),

View file

@ -58,7 +58,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
([160., 160.], true, true),
] {
parent.spawn((
UiImage {
ImageNode {
image: image.clone(),
flip_x,
flip_y,