Compare commits

...

12 Commits

Author SHA1 Message Date
Carter Anderson
6acf932cc9 Release 0.6.1 2022-02-16 12:25:43 -08:00
François
31af9d4ee1 fix timer test to be less reliant on float precision (#3789)
# Objective

- Test is failing on nightly after the merge of https://github.com/rust-lang/rust/pull/90247
- It was relying on the precision of the duration of `1.0 / 3.0`

## Solution

- Fix the test to be less reliant on float precision to have the same result
2022-02-12 21:05:53 -08:00
Robert Swain
ef5c8e83ad bevy_render: Support overriding wgpu features and limits (#3912)
# Objective

- Support overriding wgpu features and limits that were calculated from default values or queried from the adapter/backend.
- Fixes #3686

## Solution

- Add `disabled_features: Option<wgpu::Features>` to `WgpuOptions`
- Add `constrained_limits: Option<wgpu::Limits>` to `WgpuOptions`
- After maybe obtaining updated features and limits from the adapter/backend in the case of `WgpuOptionsPriority::Functionality`, enable the `WgpuOptions` `features`, disable the `disabled_features`, and constrain the `limits` by `constrained_limits`.
  - Note that constraining the limits means for `wgpu::Limits` members named `max_.*` we take the minimum of that which was configured/queried for the backend/adapter and the specified constrained limit value. This means the configured/queried value is used if the constrained limit is larger as that is as much as the device/API supports, or the constrained limit value is used if it is smaller as we are imposing an artificial constraint. For members named `min_.*` we take the maximum instead. For example, a minimum stride might be 256 but we set constrained limit value of 1024, then 1024 is the more conservative value. If the constrained limit value were 16, then 256 would be the more conservative.
2022-02-12 20:40:23 -08:00
Carter Anderson
3ae96dd307 Fix ui interactions when cursor disappears suddenly (#3926)
On platforms like wasm (on mobile) the cursor can disappear suddenly (ex: the user releases their finger from the screen). This causes the undesirable behavior in #3752. These changes make the UI handler properly handle this case.

Fixes #3752
Alternative to #3599
2022-02-12 20:36:43 -08:00
François
523e7af739 fix unreachable macro calls for rust 2021 (#3889)
# Objective

- It was decided in Rust 2021 to make macro like `panic` require a string literal to format instead of directly an object
- `unreachable` was missed during the first pass but it was decided to go for it anyway now: https://github.com/rust-lang/rust/issues/92137#issuecomment-1019519285
- this is making Bevy CI fail now: https://github.com/bevyengine/bevy/runs/5102586734?check_suite_focus=true

## Solution

- Fix calls to `unreachable`
2022-02-12 20:36:29 -08:00
TheRawMeatball
98cf6268dc Backport soundness fix (#3685)
#3001 discovered a soundness bug in World::resource_scope, this PR backports the fix with a smaller PR to patch out the bug sooner.

Fixes #3147
2022-02-12 20:36:18 -08:00
Robert Swain
ec88fcacca bevy_render: Only auto-disable mappable primary buffers for discrete GPUs (#3803)
# Objective

- While it is not safe to enable mappable primary buffers for all GPUs, it should be preferred for integrated GPUs where an integrated GPU is one that is sharing system memory.

## Solution

- Auto-disable mappable primary buffers only for discrete GPUs. If the GPU is integrated and mappable primary buffers are supported, use them.
2022-02-12 20:36:03 -08:00
Robert Swain
d11409028a bevy_pbr: Do not panic when more than 256 point lights are added the scene (#3697)
# Objective

- Do not panic when mroe than 256 point lights are added the scene
- Fixes https://github.com/bevyengine/bevy/issues/3682

## Solution

- Only iterate the first `MAX_POINT_LIGHTS` lights instead of as many as there are

## Open questions

- Should we warn that there are more than the maximum allowed number of point lights in the scene?
2022-02-12 20:35:53 -08:00
Robert Swain
df3d100315 bevy_render: Do not automatically enable MAPPABLE_PRIMARY_BUFFERS (#3698)
# Objective

- When using `WgpuOptionsPriority::Functionality`, which is the default, wgpu::Features::MAPPABLE_PRIMARY_BUFFERS would be automatically enabled. This feature can and does have a significant negative impact on performance for discrete GPUs where resizable bar is not supported, which is a common case. As such, this feature should not be automatically enabled.
- Fixes the performance regression part of https://github.com/bevyengine/bevy/issues/3686 and at least some, if not all cases of https://github.com/bevyengine/bevy/issues/3687

## Solution

- When using `WgpuOptionsPriority::Functionality`, use the adapter-supported features, enable `TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES` and disable `MAPPABLE_PRIMARY_BUFFERS`
2022-02-12 20:35:35 -08:00
François
398b15db5c do not set cursor grab on window creation if not asked for (#3617)
# Objective

- On Safari mobile, calling `winit_window.set_cursor_grab(true)` fails as the API is not implemented (as there is no cursor on Safari mobile, the api doesn't make sense there). I don't know about other mobile browsers
```
[Error] Unhandled Promise Rejection: TypeError: getObject(arg0).exitPointerLock is not a function. (In 'getObject(arg0).exitPointerLock()', 'getObject(arg0).exitPointerLock' is undefined)
    (anonymous function) (rect.js:1089)
    wasm-stub
    <?>.wasm-function[web_sys::features::gen_Document::Document::exit_pointer_lock::h20ffc49be163fc45]
    <?>.wasm-function[winit::platform_impl::platform::backend::canvas::Canvas::set_cursor_grab::h6a9472cf55263e98]
    <?>.wasm-function[bevy_winit::winit_windows::WinitWindows::create_window::h9db5b3cbb24347c5]
    <?>.wasm-function[<bevy_winit::WinitPlugin as bevy_app::plugin::Plugin>::build::ha4a7c046b80c4280]
    <?>.wasm-function[bevy_app::plugin_group::PluginGroupBuilder::finish::h0e5bc78f71c37b2f]
    <?>.wasm-function[rect::main::h899852fd17f2d489]
    <?>.wasm-function[std::sys_common::backtrace::__rust_begin_short_backtrace::hfe38f282e8dda96b]
    <?>.wasm-function[std::rt::lang_start::{{closure}}::hc2f3b555ffc58618]
    <?>.wasm-function[std::rt::lang_start_internal::ha901ae30d88554f2]
    <?>.wasm-function[main]
    <?>.wasm-function[]
    wasm-stub
    21261
    (anonymous function) (rect.js:1664)
    asyncFunctionResume
    (anonymous function)
    promiseReactionJobWithoutPromise
    promiseReactionJob
```

## Solution

- Do not call the api to release cursor grab on window creation, as the cursor is not grabbed anyway at this point
2022-02-12 20:35:19 -08:00
Robert Swain
b9337debb9 bevy_crevice: Fix incorrect iterator usage in WriteStd430 impl for [T] (#3591)
# Objective

- Fix incorrect iterator usage in WriteStd430 impl for [T]
  - The first item was being written twice. This is correct in the WriteStd140 impl for [T].

## Solution

- See the code.
2022-02-12 20:34:44 -08:00
François
470f94d19e support all line endings in shader preprocessor (#3603)
# Objective

- Advance uses of shaders seems to often fail for Windows users
- Bevy split lines on `'\n'` which messes with windows line endings

## Solution

- Uses Rust built in https://doc.rust-lang.org/std/primitive.str.html#method.lines
2022-02-12 20:34:01 -08:00
17 changed files with 176 additions and 43 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "bevy"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
categories = ["game-engines", "graphics", "gui", "rendering"]
description = "A refreshingly simple data-driven game engine and app framework"

View File

@ -469,15 +469,18 @@ mod tests {
#[test]
fn times_finished_precise() {
let mut t = Timer::from_seconds(0.01, true);
let duration = Duration::from_secs_f64(1.0 / 3.0);
let duration = Duration::from_secs_f64(0.333);
// total duration: 0.333 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 33);
// total duration: 0.666 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 33);
// total duration: 0.999 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 33);
// It has one additional tick this time to compensate for missing 100th tick
// total duration: 1.332 => 34 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 34);
}

View File

@ -1,7 +1,7 @@
[package]
name = "bevy_crevice"
description = "Create GLSL-compatible versions of structs with explicitly-initialized padding (Bevy version)"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
documentation = "https://docs.rs/crevice"

View File

@ -268,7 +268,7 @@ where
offset = item.write_std430(writer)?;
}
for item in self.iter() {
for item in iter {
item.write_std430(writer)?;
}

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_ecs"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
description = "Bevy Engine's entity component system"
homepage = "https://bevyengine.org"

View File

@ -132,7 +132,7 @@ impl ParallelSystemExecutor for ParallelExecutor {
.finish_receiver
.recv()
.await
.unwrap_or_else(|error| unreachable!(error));
.unwrap_or_else(|error| unreachable!("{}", error));
self.process_finished_system(index);
// Gather other systems than may have finished.
while let Ok(index) = self.finish_receiver.try_recv() {
@ -208,7 +208,7 @@ impl ParallelExecutor {
start_receiver
.recv()
.await
.unwrap_or_else(|error| unreachable!(error));
.unwrap_or_else(|error| unreachable!("{}", error));
#[cfg(feature = "trace")]
let system_guard = system_span.enter();
unsafe { system.run_unsafe((), world) };
@ -217,7 +217,7 @@ impl ParallelExecutor {
finish_sender
.send(index)
.await
.unwrap_or_else(|error| unreachable!(error));
.unwrap_or_else(|error| unreachable!("{}", error));
};
#[cfg(feature = "trace")]
@ -268,7 +268,7 @@ impl ParallelExecutor {
.start_sender
.send(())
.await
.unwrap_or_else(|error| unreachable!(error));
.unwrap_or_else(|error| unreachable!("{}", error));
self.running.set(index, true);
if !system_metadata.is_send {
self.non_send_running = true;

View File

@ -925,24 +925,30 @@ impl World {
// the ptr value / drop is called when T is dropped
unsafe { column.swap_remove_and_forget_unchecked(0) }
};
// SAFE: pointer is of type T
let value = Mut {
value: unsafe { &mut *ptr.cast::<T>() },
// SAFE: pointer is of type T and valid to move out of
// Read the value onto the stack to avoid potential mut aliasing.
let mut value = unsafe { std::ptr::read(ptr.cast::<T>()) };
let value_mut = Mut {
value: &mut value,
ticks: Ticks {
component_ticks: &mut ticks,
last_change_tick: self.last_change_tick(),
change_tick: self.change_tick(),
},
};
let result = f(self, value);
let result = f(self, value_mut);
assert!(!self.contains_resource::<T>());
let resource_archetype = self.archetypes.resource_mut();
let unique_components = resource_archetype.unique_components_mut();
let column = unique_components
.get_mut(component_id)
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<T>()));
// Wrap the value in MaybeUninit to prepare for passing the value back into the ECS
let mut nodrop_wrapped_value = std::mem::MaybeUninit::new(value);
unsafe {
// SAFE: pointer is of type T
column.push(ptr, ticks);
// SAFE: pointer is of type T, and valid to move out of
column.push(nodrop_wrapped_value.as_mut_ptr() as *mut _, ticks);
}
result
}

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_pbr"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
description = "Adds PBR rendering to Bevy Engine"
homepage = "https://bevyengine.org"

View File

@ -641,7 +641,7 @@ pub fn prepare_lights(
}
let mut gpu_point_lights = [GpuPointLight::default(); MAX_POINT_LIGHTS];
for (index, &(entity, light)) in point_lights.iter().enumerate() {
for (index, &(entity, light)) in point_lights.iter().enumerate().take(MAX_POINT_LIGHTS) {
let mut flags = PointLightFlags::NONE;
// Lights are sorted, shadow enabled lights are first
if light.shadows_enabled && index < MAX_POINT_LIGHT_SHADOW_MAPS {

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_render"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
description = "Provides rendering functionality for Bevy Engine"
homepage = "https://bevyengine.org"

View File

@ -15,8 +15,16 @@ pub struct WgpuOptions {
pub backends: Option<Backends>,
pub power_preference: PowerPreference,
pub priority: WgpuOptionsPriority,
/// The enabled features. Setting features will require them to be enabled when initializing
/// the renderer.
pub features: WgpuFeatures,
/// The features to ensure are disabled regardless of what the adapter/backend supports
pub disabled_features: Option<WgpuFeatures>,
/// The imposed limits. Updated based on adapter/backend limits when initializing the renderer
/// if using WgpuOptionsPriority::Functionality
pub limits: WgpuLimits,
/// The constraints on limits allowed regardless of what the adapter/backend supports
pub constrained_limits: Option<WgpuLimits>,
}
impl Default for WgpuOptions {
@ -50,7 +58,9 @@ impl Default for WgpuOptions {
power_preference: PowerPreference::HighPerformance,
priority,
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
disabled_features: None,
limits,
constrained_limits: None,
}
}
}

View File

@ -308,7 +308,7 @@ impl ShaderImportProcessor {
pub fn get_imports_from_str(&self, shader: &str) -> Vec<ShaderImport> {
let mut imports = Vec::new();
for line in shader.split('\n') {
for line in shader.lines() {
if let Some(cap) = self.import_asset_path_regex.captures(line) {
let import = cap.get(1).unwrap();
imports.push(ShaderImport::AssetPath(import.as_str().to_string()));
@ -366,7 +366,7 @@ impl ShaderProcessor {
let shader_defs_unique = HashSet::<String>::from_iter(shader_defs.iter().cloned());
let mut scopes = vec![true];
let mut final_string = String::new();
for line in shader_str.split('\n') {
for line in shader_str.lines() {
if let Some(cap) = self.ifdef_regex.captures(line) {
let def = cap.get(1).unwrap();
scopes.push(*scopes.last().unwrap() && shader_defs_unique.contains(def.as_str()));
@ -417,7 +417,6 @@ impl ShaderProcessor {
final_string.push('\n');
}
}
final_string.pop();
if scopes.len() != 1 {
return Err(ProcessShaderError::NotEnoughEndIfs);

View File

@ -73,7 +73,8 @@ pub async fn initialize_renderer(
.await
.expect("Unable to find a GPU! Make sure you have installed required drivers!");
info!("{:?}", adapter.get_info());
let adapter_info = adapter.get_info();
info!("{:?}", adapter_info);
#[cfg(feature = "wgpu_trace")]
let trace_path = {
@ -85,10 +86,120 @@ pub async fn initialize_renderer(
#[cfg(not(feature = "wgpu_trace"))]
let trace_path = None;
// Maybe get features and limits based on what is supported by the adapter/backend
let mut features = wgpu::Features::empty();
let mut limits = options.limits.clone();
if matches!(options.priority, WgpuOptionsPriority::Functionality) {
options.features =
adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
options.limits = adapter.limits();
features = adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
if adapter_info.device_type == wgpu::DeviceType::DiscreteGpu {
// `MAPPABLE_PRIMARY_BUFFERS` can have a significant, negative performance impact for
// discrete GPUs due to having to transfer data across the PCI-E bus and so it
// should not be automatically enabled in this case. It is however beneficial for
// integrated GPUs.
features -= wgpu::Features::MAPPABLE_PRIMARY_BUFFERS;
}
limits = adapter.limits();
}
// Enforce the disabled features
if let Some(disabled_features) = options.disabled_features {
features -= disabled_features;
}
// NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
options.features |= features;
// Enforce the limit constraints
if let Some(constrained_limits) = options.constrained_limits.as_ref() {
// NOTE: Respect the configured limits as an 'upper bound'. This means for 'max' limits, we
// take the minimum of the calculated limits according to the adapter/backend and the
// specified max_limits. For 'min' limits, take the maximum instead. This is intended to
// err on the side of being conservative. We can't claim 'higher' limits that are supported
// but we can constrain to 'lower' limits.
options.limits = wgpu::Limits {
max_texture_dimension_1d: limits
.max_texture_dimension_1d
.min(constrained_limits.max_texture_dimension_1d),
max_texture_dimension_2d: limits
.max_texture_dimension_2d
.min(constrained_limits.max_texture_dimension_2d),
max_texture_dimension_3d: limits
.max_texture_dimension_3d
.min(constrained_limits.max_texture_dimension_3d),
max_texture_array_layers: limits
.max_texture_array_layers
.min(constrained_limits.max_texture_array_layers),
max_bind_groups: limits
.max_bind_groups
.min(constrained_limits.max_bind_groups),
max_dynamic_uniform_buffers_per_pipeline_layout: limits
.max_dynamic_uniform_buffers_per_pipeline_layout
.min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
max_dynamic_storage_buffers_per_pipeline_layout: limits
.max_dynamic_storage_buffers_per_pipeline_layout
.min(constrained_limits.max_dynamic_storage_buffers_per_pipeline_layout),
max_sampled_textures_per_shader_stage: limits
.max_sampled_textures_per_shader_stage
.min(constrained_limits.max_sampled_textures_per_shader_stage),
max_samplers_per_shader_stage: limits
.max_samplers_per_shader_stage
.min(constrained_limits.max_samplers_per_shader_stage),
max_storage_buffers_per_shader_stage: limits
.max_storage_buffers_per_shader_stage
.min(constrained_limits.max_storage_buffers_per_shader_stage),
max_storage_textures_per_shader_stage: limits
.max_storage_textures_per_shader_stage
.min(constrained_limits.max_storage_textures_per_shader_stage),
max_uniform_buffers_per_shader_stage: limits
.max_uniform_buffers_per_shader_stage
.min(constrained_limits.max_uniform_buffers_per_shader_stage),
max_uniform_buffer_binding_size: limits
.max_uniform_buffer_binding_size
.min(constrained_limits.max_uniform_buffer_binding_size),
max_storage_buffer_binding_size: limits
.max_storage_buffer_binding_size
.min(constrained_limits.max_storage_buffer_binding_size),
max_vertex_buffers: limits
.max_vertex_buffers
.min(constrained_limits.max_vertex_buffers),
max_vertex_attributes: limits
.max_vertex_attributes
.min(constrained_limits.max_vertex_attributes),
max_vertex_buffer_array_stride: limits
.max_vertex_buffer_array_stride
.min(constrained_limits.max_vertex_buffer_array_stride),
max_push_constant_size: limits
.max_push_constant_size
.min(constrained_limits.max_push_constant_size),
min_uniform_buffer_offset_alignment: limits
.min_uniform_buffer_offset_alignment
.max(constrained_limits.min_uniform_buffer_offset_alignment),
min_storage_buffer_offset_alignment: limits
.min_storage_buffer_offset_alignment
.max(constrained_limits.min_storage_buffer_offset_alignment),
max_inter_stage_shader_components: limits
.max_inter_stage_shader_components
.min(constrained_limits.max_inter_stage_shader_components),
max_compute_workgroup_storage_size: limits
.max_compute_workgroup_storage_size
.min(constrained_limits.max_compute_workgroup_storage_size),
max_compute_invocations_per_workgroup: limits
.max_compute_invocations_per_workgroup
.min(constrained_limits.max_compute_invocations_per_workgroup),
max_compute_workgroup_size_x: limits
.max_compute_workgroup_size_x
.min(constrained_limits.max_compute_workgroup_size_x),
max_compute_workgroup_size_y: limits
.max_compute_workgroup_size_y
.min(constrained_limits.max_compute_workgroup_size_y),
max_compute_workgroup_size_z: limits
.max_compute_workgroup_size_z
.min(constrained_limits.max_compute_workgroup_size_z),
max_compute_workgroups_per_dimension: limits
.max_compute_workgroups_per_dimension
.min(constrained_limits.max_compute_workgroups_per_dimension),
};
} else {
options.limits = limits;
}
let (device, queue) = adapter

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_ui"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
description = "A custom ECS-driven UI framework built specifically for Bevy Engine"
homepage = "https://bevyengine.org"

View File

@ -72,14 +72,9 @@ pub fn ui_focus_system(
Option<&CalculatedClip>,
)>,
) {
let cursor_position = if let Some(cursor_position) = windows
let cursor_position = windows
.get_primary()
.and_then(|window| window.cursor_position())
{
cursor_position
} else {
return;
};
.and_then(|window| window.cursor_position());
// reset entities that were both clicked and released in the last frame
for entity in state.entities_to_reset.drain(..) {
@ -120,13 +115,20 @@ pub fn ui_focus_system(
}
// if the current cursor position is within the bounds of the node, consider it for
// clicking
if (min.x..max.x).contains(&cursor_position.x)
&& (min.y..max.y).contains(&cursor_position.y)
{
let contains_cursor = if let Some(cursor_position) = cursor_position {
(min.x..max.x).contains(&cursor_position.x)
&& (min.y..max.y).contains(&cursor_position.y)
} else {
false
};
if contains_cursor {
Some((entity, focus_policy, interaction, FloatOrd(position.z)))
} else {
if let Some(mut interaction) = interaction {
if *interaction == Interaction::Hovered {
if *interaction == Interaction::Hovered
|| (cursor_position.is_none() && *interaction != Interaction::None)
{
*interaction = Interaction::None;
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "bevy_winit"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
description = "A winit window and input backend for Bevy Engine"
homepage = "https://bevyengine.org"

View File

@ -127,10 +127,12 @@ impl WinitWindows {
let winit_window = winit_window_builder.build(event_loop).unwrap();
match winit_window.set_cursor_grab(window_descriptor.cursor_locked) {
Ok(_) => {}
Err(winit::error::ExternalError::NotSupported(_)) => {}
Err(err) => Err(err).unwrap(),
if window_descriptor.cursor_locked {
match winit_window.set_cursor_grab(true) {
Ok(_) => {}
Err(winit::error::ExternalError::NotSupported(_)) => {}
Err(err) => Err(err).unwrap(),
}
}
winit_window.set_cursor_visible(window_descriptor.cursor_visible);