Merge branch 'main' into dlss3
This commit is contained in:
commit
6f9114be60
@ -18,12 +18,14 @@ bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
||||
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
||||
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
||||
bevy_picking = { path = "../bevy_picking", version = "0.17.0-dev" }
|
||||
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
||||
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
|
||||
bevy_text = { path = "../bevy_text", version = "0.17.0-dev" }
|
||||
bevy_ui = { path = "../bevy_ui", version = "0.17.0-dev" }
|
||||
bevy_ui_render = { path = "../bevy_ui_render", version = "0.17.0-dev" }
|
||||
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
|
||||
bevy_state = { path = "../bevy_state", version = "0.17.0-dev" }
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Module containing logic for FPS overlay.
|
||||
|
||||
use bevy_app::{Plugin, Startup, Update};
|
||||
use bevy_asset::Handle;
|
||||
use bevy_asset::{Assets, Handle};
|
||||
use bevy_color::Color;
|
||||
use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
|
||||
use bevy_ecs::{
|
||||
@ -12,22 +12,31 @@ use bevy_ecs::{
|
||||
query::With,
|
||||
resource::Resource,
|
||||
schedule::{common_conditions::resource_changed, IntoScheduleConfigs},
|
||||
system::{Commands, Query, Res},
|
||||
system::{Commands, Query, Res, ResMut},
|
||||
};
|
||||
use bevy_render::view::Visibility;
|
||||
use bevy_render::{storage::ShaderStorageBuffer, view::Visibility};
|
||||
use bevy_text::{Font, TextColor, TextFont, TextSpan};
|
||||
use bevy_time::Time;
|
||||
use bevy_ui::{
|
||||
widget::{Text, TextUiWriter},
|
||||
GlobalZIndex, Node, PositionType,
|
||||
FlexDirection, GlobalZIndex, Node, PositionType, Val,
|
||||
};
|
||||
use bevy_ui_render::prelude::MaterialNode;
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::frame_time_graph::{
|
||||
FrameTimeGraphConfigUniform, FrameTimeGraphPlugin, FrametimeGraphMaterial,
|
||||
};
|
||||
|
||||
/// [`GlobalZIndex`] used to render the fps overlay.
|
||||
///
|
||||
/// We use a number slightly under `i32::MAX` so you can render on top of it if you really need to.
|
||||
pub const FPS_OVERLAY_ZINDEX: i32 = i32::MAX - 32;
|
||||
|
||||
// Used to scale the frame time graph based on the fps text size
|
||||
const FRAME_TIME_GRAPH_WIDTH_SCALE: f32 = 6.0;
|
||||
const FRAME_TIME_GRAPH_HEIGHT_SCALE: f32 = 2.0;
|
||||
|
||||
/// A plugin that adds an FPS overlay to the Bevy application.
|
||||
///
|
||||
/// This plugin will add the [`FrameTimeDiagnosticsPlugin`] if it wasn't added before.
|
||||
@ -47,12 +56,18 @@ impl Plugin for FpsOverlayPlugin {
|
||||
if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {
|
||||
app.add_plugins(FrameTimeDiagnosticsPlugin::default());
|
||||
}
|
||||
|
||||
if !app.is_plugin_added::<FrameTimeGraphPlugin>() {
|
||||
app.add_plugins(FrameTimeGraphPlugin);
|
||||
}
|
||||
|
||||
app.insert_resource(self.config.clone())
|
||||
.add_systems(Startup, setup)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
(customize_text, toggle_display).run_if(resource_changed::<FpsOverlayConfig>),
|
||||
(toggle_display, customize_overlay)
|
||||
.run_if(resource_changed::<FpsOverlayConfig>),
|
||||
update_text,
|
||||
),
|
||||
);
|
||||
@ -72,6 +87,8 @@ pub struct FpsOverlayConfig {
|
||||
///
|
||||
/// Defaults to once every 100 ms.
|
||||
pub refresh_interval: Duration,
|
||||
/// Configuration of the frame time graph
|
||||
pub frame_time_graph_config: FrameTimeGraphConfig,
|
||||
}
|
||||
|
||||
impl Default for FpsOverlayConfig {
|
||||
@ -85,6 +102,43 @@ impl Default for FpsOverlayConfig {
|
||||
text_color: Color::WHITE,
|
||||
enabled: true,
|
||||
refresh_interval: Duration::from_millis(100),
|
||||
// TODO set this to display refresh rate if possible
|
||||
frame_time_graph_config: FrameTimeGraphConfig::target_fps(60.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of the frame time graph
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct FrameTimeGraphConfig {
|
||||
/// Is the graph visible
|
||||
pub enabled: bool,
|
||||
/// The minimum acceptable FPS
|
||||
///
|
||||
/// Anything below this will show a red bar
|
||||
pub min_fps: f32,
|
||||
/// The target FPS
|
||||
///
|
||||
/// Anything above this will show a green bar
|
||||
pub target_fps: f32,
|
||||
}
|
||||
|
||||
impl FrameTimeGraphConfig {
|
||||
/// Constructs a default config for a given target fps
|
||||
pub fn target_fps(target_fps: f32) -> Self {
|
||||
Self {
|
||||
target_fps,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FrameTimeGraphConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
min_fps: 30.0,
|
||||
target_fps: 60.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,12 +146,21 @@ impl Default for FpsOverlayConfig {
|
||||
#[derive(Component)]
|
||||
struct FpsText;
|
||||
|
||||
fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
|
||||
#[derive(Component)]
|
||||
struct FrameTimeGraph;
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
overlay_config: Res<FpsOverlayConfig>,
|
||||
mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,
|
||||
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
|
||||
) {
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
// We need to make sure the overlay doesn't affect the position of other UI nodes
|
||||
position_type: PositionType::Absolute,
|
||||
flex_direction: FlexDirection::Column,
|
||||
..Default::default()
|
||||
},
|
||||
// Render overlay on top of everything
|
||||
@ -111,6 +174,29 @@ fn setup(mut commands: Commands, overlay_config: Res<FpsOverlayConfig>) {
|
||||
FpsText,
|
||||
))
|
||||
.with_child((TextSpan::default(), overlay_config.text_config.clone()));
|
||||
|
||||
let font_size = overlay_config.text_config.font_size;
|
||||
p.spawn((
|
||||
Node {
|
||||
width: Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE),
|
||||
height: Val::Px(font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE),
|
||||
display: if overlay_config.frame_time_graph_config.enabled {
|
||||
bevy_ui::Display::DEFAULT
|
||||
} else {
|
||||
bevy_ui::Display::None
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
MaterialNode::from(frame_time_graph_materials.add(FrametimeGraphMaterial {
|
||||
values: buffers.add(ShaderStorageBuffer::default()),
|
||||
config: FrameTimeGraphConfigUniform::new(
|
||||
overlay_config.frame_time_graph_config.target_fps,
|
||||
overlay_config.frame_time_graph_config.min_fps,
|
||||
true,
|
||||
),
|
||||
})),
|
||||
FrameTimeGraph,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@ -135,7 +221,7 @@ fn update_text(
|
||||
}
|
||||
}
|
||||
|
||||
fn customize_text(
|
||||
fn customize_overlay(
|
||||
overlay_config: Res<FpsOverlayConfig>,
|
||||
query: Query<Entity, With<FpsText>>,
|
||||
mut writer: TextUiWriter,
|
||||
@ -151,6 +237,7 @@ fn customize_text(
|
||||
fn toggle_display(
|
||||
overlay_config: Res<FpsOverlayConfig>,
|
||||
mut query: Query<&mut Visibility, With<FpsText>>,
|
||||
mut graph_style: Query<&mut Node, With<FrameTimeGraph>>,
|
||||
) {
|
||||
for mut visibility in &mut query {
|
||||
visibility.set_if_neq(match overlay_config.enabled {
|
||||
@ -158,4 +245,17 @@ fn toggle_display(
|
||||
false => Visibility::Hidden,
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(mut graph_style) = graph_style.single_mut() {
|
||||
if overlay_config.frame_time_graph_config.enabled {
|
||||
// Scale the frame time graph based on the font size of the overlay
|
||||
let font_size = overlay_config.text_config.font_size;
|
||||
graph_style.width = Val::Px(font_size * FRAME_TIME_GRAPH_WIDTH_SCALE);
|
||||
graph_style.height = Val::Px(font_size * FRAME_TIME_GRAPH_HEIGHT_SCALE);
|
||||
|
||||
graph_style.display = bevy_ui::Display::DEFAULT;
|
||||
} else {
|
||||
graph_style.display = bevy_ui::Display::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
#import bevy_ui::ui_vertex_output::UiVertexOutput
|
||||
|
||||
@group(1) @binding(0) var<storage> values: array<f32>;
|
||||
struct Config {
|
||||
dt_min: f32,
|
||||
dt_max: f32,
|
||||
dt_min_log2: f32,
|
||||
dt_max_log2: f32,
|
||||
proportional_width: u32,
|
||||
}
|
||||
@group(1) @binding(1) var<uniform> config: Config;
|
||||
|
||||
const RED: vec4<f32> = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
const GREEN: vec4<f32> = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
|
||||
// Gets a color based on the delta time
|
||||
// TODO use customizable gradient
|
||||
fn color_from_dt(dt: f32) -> vec4<f32> {
|
||||
return mix(GREEN, RED, dt / config.dt_max);
|
||||
}
|
||||
|
||||
// Draw an SDF square
|
||||
fn sdf_square(pos: vec2<f32>, half_size: vec2<f32>, offset: vec2<f32>) -> f32 {
|
||||
let p = pos - offset;
|
||||
let dist = abs(p) - half_size;
|
||||
let outside_dist = length(max(dist, vec2<f32>(0.0, 0.0)));
|
||||
let inside_dist = min(max(dist.x, dist.y), 0.0);
|
||||
return outside_dist + inside_dist;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(in: UiVertexOutput) -> @location(0) vec4<f32> {
|
||||
let dt_min = config.dt_min;
|
||||
let dt_max = config.dt_max;
|
||||
let dt_min_log2 = config.dt_min_log2;
|
||||
let dt_max_log2 = config.dt_max_log2;
|
||||
|
||||
// The general algorithm is highly inspired by
|
||||
// <https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times>
|
||||
|
||||
let len = arrayLength(&values);
|
||||
var graph_width = 0.0;
|
||||
for (var i = 0u; i <= len; i += 1u) {
|
||||
let dt = values[len - i];
|
||||
|
||||
var frame_width: f32;
|
||||
if config.proportional_width == 1u {
|
||||
frame_width = (dt / dt_min) / f32(len);
|
||||
} else {
|
||||
frame_width = 0.015;
|
||||
}
|
||||
|
||||
let frame_height_factor = (log2(dt) - dt_min_log2) / (dt_max_log2 - dt_min_log2);
|
||||
let frame_height_factor_norm = min(max(0.0, frame_height_factor), 1.0);
|
||||
let frame_height = mix(0.0, 1.0, frame_height_factor_norm);
|
||||
|
||||
let size = vec2(frame_width, frame_height) / 2.0;
|
||||
let offset = vec2(1.0 - graph_width - size.x, 1. - size.y);
|
||||
if (sdf_square(in.uv, size, offset) < 0.0) {
|
||||
return color_from_dt(dt);
|
||||
}
|
||||
|
||||
graph_width += frame_width;
|
||||
}
|
||||
|
||||
return vec4(0.0, 0.0, 0.0, 0.5);
|
||||
}
|
||||
|
114
crates/bevy_dev_tools/src/frame_time_graph/mod.rs
Normal file
114
crates/bevy_dev_tools/src/frame_time_graph/mod.rs
Normal file
@ -0,0 +1,114 @@
|
||||
//! Module containing logic for the frame time graph
|
||||
|
||||
use bevy_app::{Plugin, Update};
|
||||
use bevy_asset::{load_internal_asset, uuid_handle, Asset, Assets, Handle};
|
||||
use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
|
||||
use bevy_ecs::system::{Res, ResMut};
|
||||
use bevy_math::ops::log2;
|
||||
use bevy_reflect::TypePath;
|
||||
use bevy_render::{
|
||||
render_resource::{AsBindGroup, Shader, ShaderRef, ShaderType},
|
||||
storage::ShaderStorageBuffer,
|
||||
};
|
||||
use bevy_ui_render::prelude::{UiMaterial, UiMaterialPlugin};
|
||||
|
||||
use crate::fps_overlay::FpsOverlayConfig;
|
||||
|
||||
const FRAME_TIME_GRAPH_SHADER_HANDLE: Handle<Shader> =
|
||||
uuid_handle!("4e38163a-5782-47a5-af52-d9161472ab59");
|
||||
|
||||
/// Plugin that sets up everything to render the frame time graph material
|
||||
pub struct FrameTimeGraphPlugin;
|
||||
|
||||
impl Plugin for FrameTimeGraphPlugin {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
load_internal_asset!(
|
||||
app,
|
||||
FRAME_TIME_GRAPH_SHADER_HANDLE,
|
||||
"frame_time_graph.wgsl",
|
||||
Shader::from_wgsl
|
||||
);
|
||||
|
||||
// TODO: Use plugin dependencies, see https://github.com/bevyengine/bevy/issues/69
|
||||
if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {
|
||||
panic!("Requires FrameTimeDiagnosticsPlugin");
|
||||
// app.add_plugins(FrameTimeDiagnosticsPlugin);
|
||||
}
|
||||
|
||||
app.add_plugins(UiMaterialPlugin::<FrametimeGraphMaterial>::default())
|
||||
.add_systems(Update, update_frame_time_values);
|
||||
}
|
||||
}
|
||||
|
||||
/// The config values sent to the frame time graph shader
|
||||
#[derive(Debug, Clone, Copy, ShaderType)]
|
||||
pub struct FrameTimeGraphConfigUniform {
|
||||
// minimum expected delta time
|
||||
dt_min: f32,
|
||||
// maximum expected delta time
|
||||
dt_max: f32,
|
||||
dt_min_log2: f32,
|
||||
dt_max_log2: f32,
|
||||
// controls whether or not the bars width are proportional to their delta time
|
||||
proportional_width: u32,
|
||||
}
|
||||
|
||||
impl FrameTimeGraphConfigUniform {
|
||||
/// `proportional_width`: controls whether or not the bars width are proportional to their delta time
|
||||
pub fn new(target_fps: f32, min_fps: f32, proportional_width: bool) -> Self {
|
||||
// we want an upper limit that is above the target otherwise the bars will disappear
|
||||
let dt_min = 1. / (target_fps * 1.2);
|
||||
let dt_max = 1. / min_fps;
|
||||
Self {
|
||||
dt_min,
|
||||
dt_max,
|
||||
dt_min_log2: log2(dt_min),
|
||||
dt_max_log2: log2(dt_max),
|
||||
proportional_width: u32::from(proportional_width),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The material used to render the frame time graph ui node
|
||||
#[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
|
||||
pub struct FrametimeGraphMaterial {
|
||||
/// The history of the previous frame times value.
|
||||
///
|
||||
/// This should be updated every frame to match the frame time history from the [`DiagnosticsStore`]
|
||||
#[storage(0, read_only)]
|
||||
pub values: Handle<ShaderStorageBuffer>, // Vec<f32>,
|
||||
/// The configuration values used by the shader to control how the graph is rendered
|
||||
#[uniform(1)]
|
||||
pub config: FrameTimeGraphConfigUniform,
|
||||
}
|
||||
|
||||
impl UiMaterial for FrametimeGraphMaterial {
|
||||
fn fragment_shader() -> ShaderRef {
|
||||
FRAME_TIME_GRAPH_SHADER_HANDLE.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// A system that updates the frame time values sent to the frame time graph
|
||||
fn update_frame_time_values(
|
||||
mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,
|
||||
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
|
||||
diagnostics_store: Res<DiagnosticsStore>,
|
||||
config: Option<Res<FpsOverlayConfig>>,
|
||||
) {
|
||||
if !config.is_none_or(|c| c.frame_time_graph_config.enabled) {
|
||||
return;
|
||||
}
|
||||
let Some(frame_time) = diagnostics_store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else {
|
||||
return;
|
||||
};
|
||||
let frame_times = frame_time
|
||||
.values()
|
||||
// convert to millis
|
||||
.map(|x| *x as f32 / 1000.0)
|
||||
.collect::<Vec<_>>();
|
||||
for (_, material) in frame_time_graph_materials.iter_mut() {
|
||||
let buffer = buffers.get_mut(&material.values).unwrap();
|
||||
|
||||
buffer.set_data(frame_times.clone().as_slice());
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ use bevy_app::prelude::*;
|
||||
pub mod ci_testing;
|
||||
|
||||
pub mod fps_overlay;
|
||||
pub mod frame_time_graph;
|
||||
|
||||
pub mod picking_debug;
|
||||
|
||||
|
@ -746,11 +746,11 @@ fn early_sweep_material_instances<M>(
|
||||
/// preparation for a new frame.
|
||||
pub(crate) fn late_sweep_material_instances(
|
||||
mut material_instances: ResMut<RenderMaterialInstances>,
|
||||
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
|
||||
mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
|
||||
) {
|
||||
let last_change_tick = material_instances.current_change_tick;
|
||||
|
||||
for entity in removed_visibilities_query.read() {
|
||||
for entity in removed_meshes_query.read() {
|
||||
if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) {
|
||||
// Only sweep the entry if it wasn't updated this frame. It's
|
||||
// possible that a `ViewVisibility` component was removed and
|
||||
|
@ -1452,8 +1452,6 @@ pub fn extract_meshes_for_gpu_building(
|
||||
>,
|
||||
>,
|
||||
all_meshes_query: Extract<Query<GpuMeshExtractionQuery>>,
|
||||
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
|
||||
mut removed_global_transforms_query: Extract<RemovedComponents<GlobalTransform>>,
|
||||
mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
|
||||
gpu_culling_query: Extract<Query<(), (With<Camera>, Without<NoIndirectDrawing>)>>,
|
||||
meshes_to_reextract_next_frame: ResMut<MeshesToReextractNextFrame>,
|
||||
@ -1509,11 +1507,7 @@ pub fn extract_meshes_for_gpu_building(
|
||||
}
|
||||
|
||||
// Also record info about each mesh that became invisible.
|
||||
for entity in removed_visibilities_query
|
||||
.read()
|
||||
.chain(removed_global_transforms_query.read())
|
||||
.chain(removed_meshes_query.read())
|
||||
{
|
||||
for entity in removed_meshes_query.read() {
|
||||
// Only queue a mesh for removal if we didn't pick it up above.
|
||||
// It's possible that a necessary component was removed and re-added in
|
||||
// the same frame.
|
||||
|
@ -62,6 +62,22 @@ fn fetch_point_shadow(light_id: u32, frag_position: vec4<f32>, surface_normal: v
|
||||
return sample_shadow_cubemap(frag_ls * flip_z, distance_to_light, depth, light_id);
|
||||
}
|
||||
|
||||
// this method of constructing a basis from a vec3 is used by glam::Vec3::any_orthonormal_pair
|
||||
// so we reproduce it here to avoid a mismatch if glam changes. we also switch the handedness
|
||||
// the construction of the orthonormal basis up and right vectors needs to precisely mirror the code
|
||||
// in bevy_light/spot_light.rs:spot_light_world_from_view
|
||||
fn spot_light_world_from_view(fwd: vec3<f32>) -> mat3x3<f32> {
|
||||
var sign = -1.0;
|
||||
if (fwd.z >= 0.0) {
|
||||
sign = 1.0;
|
||||
}
|
||||
let a = -1.0 / (fwd.z + sign);
|
||||
let b = fwd.x * fwd.y * a;
|
||||
let up_dir = vec3<f32>(1.0 + sign * fwd.x * fwd.x * a, sign * b, -sign * fwd.x);
|
||||
let right_dir = vec3<f32>(-b, -sign - fwd.y * fwd.y * a, fwd.y);
|
||||
return mat3x3<f32>(right_dir, up_dir, fwd);
|
||||
}
|
||||
|
||||
fn fetch_spot_shadow(
|
||||
light_id: u32,
|
||||
frag_position: vec4<f32>,
|
||||
@ -88,17 +104,7 @@ fn fetch_spot_shadow(
|
||||
+ ((*light).shadow_depth_bias * normalize(surface_to_light))
|
||||
+ (surface_normal.xyz * (*light).shadow_normal_bias) * distance_to_light;
|
||||
|
||||
// the construction of the up and right vectors needs to precisely mirror the code
|
||||
// in render/light.rs:spot_light_view_matrix
|
||||
var sign = -1.0;
|
||||
if (fwd.z >= 0.0) {
|
||||
sign = 1.0;
|
||||
}
|
||||
let a = -1.0 / (fwd.z + sign);
|
||||
let b = fwd.x * fwd.y * a;
|
||||
let up_dir = vec3<f32>(1.0 + sign * fwd.x * fwd.x * a, sign * b, -sign * fwd.x);
|
||||
let right_dir = vec3<f32>(-b, -sign - fwd.y * fwd.y * a, fwd.y);
|
||||
let light_inv_rot = mat3x3<f32>(right_dir, up_dir, fwd);
|
||||
let light_inv_rot = spot_light_world_from_view(fwd);
|
||||
|
||||
// because the matrix is a pure rotation matrix, the inverse is just the transpose, and to calculate
|
||||
// the product of the transpose with a vector we can just post-multiply instead of pre-multiplying.
|
||||
|
@ -309,7 +309,6 @@ pub fn extract_skins(
|
||||
skinned_mesh_inverse_bindposes: Extract<Res<Assets<SkinnedMeshInverseBindposes>>>,
|
||||
changed_transforms: Extract<Query<(Entity, &GlobalTransform), Changed<GlobalTransform>>>,
|
||||
joints: Extract<Query<&GlobalTransform>>,
|
||||
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
|
||||
mut removed_skinned_meshes_query: Extract<RemovedComponents<SkinnedMesh>>,
|
||||
) {
|
||||
let skin_uniforms = skin_uniforms.into_inner();
|
||||
@ -335,10 +334,7 @@ pub fn extract_skins(
|
||||
);
|
||||
|
||||
// Delete skins that became invisible.
|
||||
for skinned_mesh_entity in removed_visibilities_query
|
||||
.read()
|
||||
.chain(removed_skinned_meshes_query.read())
|
||||
{
|
||||
for skinned_mesh_entity in removed_skinned_meshes_query.read() {
|
||||
// Only remove a skin if we didn't pick it up in `add_or_delete_skins`.
|
||||
// It's possible that a necessary component was removed and re-added in
|
||||
// the same frame.
|
||||
|
@ -331,7 +331,6 @@ pub fn extract_mesh_materials_2d<M: Material2d>(
|
||||
Or<(Changed<ViewVisibility>, Changed<MeshMaterial2d<M>>)>,
|
||||
>,
|
||||
>,
|
||||
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
|
||||
mut removed_materials_query: Extract<RemovedComponents<MeshMaterial2d<M>>>,
|
||||
) {
|
||||
for (entity, view_visibility, material) in &changed_meshes_query {
|
||||
@ -342,10 +341,7 @@ pub fn extract_mesh_materials_2d<M: Material2d>(
|
||||
}
|
||||
}
|
||||
|
||||
for entity in removed_visibilities_query
|
||||
.read()
|
||||
.chain(removed_materials_query.read())
|
||||
{
|
||||
for entity in removed_materials_query.read() {
|
||||
// Only queue a mesh for removal if we didn't pick it up above.
|
||||
// It's possible that a necessary component was removed and re-added in
|
||||
// the same frame.
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Showcase how to use and configure FPS overlay.
|
||||
|
||||
use bevy::{
|
||||
dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin},
|
||||
dev_tools::fps_overlay::{FpsOverlayConfig, FpsOverlayPlugin, FrameTimeGraphConfig},
|
||||
prelude::*,
|
||||
text::FontSmoothing,
|
||||
};
|
||||
@ -33,6 +33,13 @@ fn main() {
|
||||
// We can also set the refresh interval for the FPS counter
|
||||
refresh_interval: core::time::Duration::from_millis(100),
|
||||
enabled: true,
|
||||
frame_time_graph_config: FrameTimeGraphConfig {
|
||||
enabled: true,
|
||||
// The minimum acceptable fps
|
||||
min_fps: 30.0,
|
||||
// The target fps
|
||||
target_fps: 144.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
))
|
||||
@ -52,7 +59,8 @@ fn setup(mut commands: Commands) {
|
||||
"Press 1 to toggle the overlay color.\n",
|
||||
"Press 2 to decrease the overlay size.\n",
|
||||
"Press 3 to increase the overlay size.\n",
|
||||
"Press 4 to toggle the overlay visibility."
|
||||
"Press 4 to toggle the text visibility.\n",
|
||||
"Press 5 to toggle the frame time graph."
|
||||
)),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
@ -81,4 +89,7 @@ fn customize_config(input: Res<ButtonInput<KeyCode>>, mut overlay: ResMut<FpsOve
|
||||
if input.just_pressed(KeyCode::Digit4) {
|
||||
overlay.enabled = !overlay.enabled;
|
||||
}
|
||||
if input.just_released(KeyCode::Digit5) {
|
||||
overlay.frame_time_graph_config.enabled = !overlay.frame_time_graph_config.enabled;
|
||||
}
|
||||
}
|
||||
|
18
release-content/release-notes/frame_time_graph.md
Normal file
18
release-content/release-notes/frame_time_graph.md
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
title: Frame Time Graph
|
||||
authors: ["@IceSentry", "@Zeophlite"]
|
||||
pull_requests: [12561, 19277]
|
||||
---
|
||||
|
||||
(TODO: Embed frame time graph gif from 12561)
|
||||
|
||||
Frame time is often more important to know than FPS but because of the temporal nature of it, just seeing a number is not enough.
|
||||
Seeing a graph that shows the history makes it easier to reason about performance.
|
||||
|
||||
Enable the `bevy_dev_tools` feature, and add in `FpsOverlayPlugin` to add a bar graph of the frame time history.
|
||||
Each bar is scaled based on the frame time where a bigger frame time will give a taller and wider bar.
|
||||
|
||||
The color also scales with that frame time where red is at or bellow the minimum target fps and green is at or above the target maximum frame rate.
|
||||
Anything between those 2 values will be interpolated between green and red based on the frame time.
|
||||
|
||||
The algorithm is highly inspired by [Adam Sawicki's article on visualizing frame times](https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times).
|
Loading…
Reference in New Issue
Block a user