Merge 00f06f9c51
into f964ee1e3a
This commit is contained in:
commit
f9cc7d102a
124
crates/bevy_pbr/src/diagnostic.rs
Normal file
124
crates/bevy_pbr/src/diagnostic.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use core::{
|
||||
any::{type_name, Any, TypeId},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use bevy_app::{Plugin, PreUpdate};
|
||||
use bevy_diagnostic::{Diagnostic, DiagnosticPath, Diagnostics, RegisterDiagnostic};
|
||||
use bevy_ecs::{resource::Resource, system::Res};
|
||||
use bevy_platform::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use bevy_render::{Extract, ExtractSchedule, RenderApp};
|
||||
|
||||
use crate::{Material, MaterialBindGroupAllocators};
|
||||
|
||||
pub struct MaterialAllocatorDiagnosticPlugin<M: Material> {
|
||||
suffix: &'static str,
|
||||
_phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<M: Material> MaterialAllocatorDiagnosticPlugin<M> {
|
||||
pub fn new(suffix: &'static str) -> Self {
|
||||
Self {
|
||||
suffix,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Material> Default for MaterialAllocatorDiagnosticPlugin<M> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
suffix: " materials",
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Material> MaterialAllocatorDiagnosticPlugin<M> {
|
||||
/// Get the [`DiagnosticPath`] for slab count
|
||||
pub fn slabs_diagnostic_path() -> DiagnosticPath {
|
||||
DiagnosticPath::from_components(["material_allocator_slabs", type_name::<M>()])
|
||||
}
|
||||
/// Get the [`DiagnosticPath`] for total slabs size
|
||||
pub fn slabs_size_diagnostic_path() -> DiagnosticPath {
|
||||
DiagnosticPath::from_components(["material_allocator_slabs_size", type_name::<M>()])
|
||||
}
|
||||
/// Get the [`DiagnosticPath`] for material allocations
|
||||
pub fn allocations_diagnostic_path() -> DiagnosticPath {
|
||||
DiagnosticPath::from_components(["material_allocator_allocations", type_name::<M>()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Material> Plugin for MaterialAllocatorDiagnosticPlugin<M> {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.register_diagnostic(
|
||||
Diagnostic::new(Self::slabs_diagnostic_path()).with_suffix(" slabs"),
|
||||
)
|
||||
.register_diagnostic(
|
||||
Diagnostic::new(Self::slabs_size_diagnostic_path()).with_suffix(" bytes"),
|
||||
)
|
||||
.register_diagnostic(
|
||||
Diagnostic::new(Self::allocations_diagnostic_path()).with_suffix(self.suffix),
|
||||
)
|
||||
.init_resource::<MaterialAllocatorMeasurements<M>>()
|
||||
.add_systems(PreUpdate, add_material_allocator_measurement::<M>);
|
||||
|
||||
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app.add_systems(ExtractSchedule, measure_allocator::<M>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Resource)]
|
||||
struct MaterialAllocatorMeasurements<M: Material> {
|
||||
slabs: AtomicUsize,
|
||||
slabs_size: AtomicUsize,
|
||||
allocations: AtomicU64,
|
||||
_phantom: PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<M: Material> Default for MaterialAllocatorMeasurements<M> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
slabs: AtomicUsize::default(),
|
||||
slabs_size: AtomicUsize::default(),
|
||||
allocations: AtomicU64::default(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_material_allocator_measurement<M: Material>(
|
||||
mut diagnostics: Diagnostics,
|
||||
measurements: Res<MaterialAllocatorMeasurements<M>>,
|
||||
) {
|
||||
diagnostics.add_measurement(
|
||||
&MaterialAllocatorDiagnosticPlugin::<M>::slabs_diagnostic_path(),
|
||||
|| measurements.slabs.load(Ordering::Relaxed) as f64,
|
||||
);
|
||||
diagnostics.add_measurement(
|
||||
&MaterialAllocatorDiagnosticPlugin::<M>::slabs_size_diagnostic_path(),
|
||||
|| measurements.slabs_size.load(Ordering::Relaxed) as f64,
|
||||
);
|
||||
diagnostics.add_measurement(
|
||||
&MaterialAllocatorDiagnosticPlugin::<M>::allocations_diagnostic_path(),
|
||||
|| measurements.allocations.load(Ordering::Relaxed) as f64,
|
||||
);
|
||||
}
|
||||
|
||||
fn measure_allocator<M: Material + Any>(
|
||||
measurements: Extract<Res<MaterialAllocatorMeasurements<M>>>,
|
||||
allocators: Res<MaterialBindGroupAllocators>,
|
||||
) {
|
||||
if let Some(allocator) = allocators.get(&TypeId::of::<M>()) {
|
||||
measurements
|
||||
.slabs
|
||||
.store(allocator.slab_count(), Ordering::Relaxed);
|
||||
measurements
|
||||
.slabs_size
|
||||
.store(allocator.slabs_size(), Ordering::Relaxed);
|
||||
measurements
|
||||
.allocations
|
||||
.store(allocator.allocations(), Ordering::Relaxed);
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ mod cluster;
|
||||
mod components;
|
||||
pub mod decal;
|
||||
pub mod deferred;
|
||||
pub mod diagnostic;
|
||||
mod extended_material;
|
||||
mod fog;
|
||||
mod light_probe;
|
||||
|
@ -590,6 +590,45 @@ impl MaterialBindGroupAllocator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get number of allocated slabs for bindless material, returns 0 if it is
|
||||
/// [`Self::NonBindless`].
|
||||
pub fn slab_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Bindless(bless) => bless.slabs.len(),
|
||||
Self::NonBindless(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get total size of slabs allocated for bindless material, returns 0 if it is
|
||||
/// [`Self::NonBindless`].
|
||||
pub fn slabs_size(&self) -> usize {
|
||||
match self {
|
||||
Self::Bindless(bless) => bless
|
||||
.slabs
|
||||
.iter()
|
||||
.flat_map(|slab| {
|
||||
slab.data_buffers
|
||||
.iter()
|
||||
.map(|(_, buffer)| buffer.buffer.len())
|
||||
})
|
||||
.sum(),
|
||||
Self::NonBindless(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get number of bindless material allocations in slabs, returns 0 if it is
|
||||
/// [`Self::NonBindless`].
|
||||
pub fn allocations(&self) -> u64 {
|
||||
match self {
|
||||
Self::Bindless(bless) => bless
|
||||
.slabs
|
||||
.iter()
|
||||
.map(|slab| u64::from(slab.allocated_resource_count))
|
||||
.sum(),
|
||||
Self::NonBindless(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaterialBindlessIndexTable {
|
||||
|
@ -0,0 +1,91 @@
|
||||
use bevy_app::{Plugin, PreUpdate};
|
||||
use bevy_diagnostic::{Diagnostic, DiagnosticPath, Diagnostics, RegisterDiagnostic};
|
||||
use bevy_ecs::{resource::Resource, system::Res};
|
||||
use bevy_platform::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
|
||||
use crate::{mesh::allocator::MeshAllocator, Extract, ExtractSchedule, RenderApp};
|
||||
|
||||
/// Number of meshes allocated by the allocator
|
||||
static MESH_ALLOCATOR_SLABS: DiagnosticPath = DiagnosticPath::const_new("mesh_allocator_slabs");
|
||||
|
||||
/// Total size of all slabs
|
||||
static MESH_ALLOCATOR_SLABS_SIZE: DiagnosticPath =
|
||||
DiagnosticPath::const_new("mesh_allocator_slabs_size");
|
||||
|
||||
/// Number of meshes allocated into slabs
|
||||
static MESH_ALLOCATOR_ALLOCATIONS: DiagnosticPath =
|
||||
DiagnosticPath::const_new("mesh_allocator_allocations");
|
||||
|
||||
pub struct MeshAllocatorDiagnosticPlugin;
|
||||
|
||||
impl MeshAllocatorDiagnosticPlugin {
|
||||
/// Get the [`DiagnosticPath`] for slab count
|
||||
pub fn slabs_diagnostic_path() -> &'static DiagnosticPath {
|
||||
&MESH_ALLOCATOR_SLABS
|
||||
}
|
||||
/// Get the [`DiagnosticPath`] for total slabs size
|
||||
pub fn slabs_size_diagnostic_path() -> &'static DiagnosticPath {
|
||||
&MESH_ALLOCATOR_SLABS_SIZE
|
||||
}
|
||||
/// Get the [`DiagnosticPath`] for mesh allocations
|
||||
pub fn allocations_diagnostic_path() -> &'static DiagnosticPath {
|
||||
&MESH_ALLOCATOR_ALLOCATIONS
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for MeshAllocatorDiagnosticPlugin {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.register_diagnostic(
|
||||
Diagnostic::new(MESH_ALLOCATOR_SLABS.clone()).with_suffix(" slabs"),
|
||||
)
|
||||
.register_diagnostic(
|
||||
Diagnostic::new(MESH_ALLOCATOR_SLABS_SIZE.clone()).with_suffix(" bytes"),
|
||||
)
|
||||
.register_diagnostic(
|
||||
Diagnostic::new(MESH_ALLOCATOR_ALLOCATIONS.clone()).with_suffix(" meshes"),
|
||||
)
|
||||
.init_resource::<MeshAllocatorMeasurements>()
|
||||
.add_systems(PreUpdate, add_mesh_allocator_measurement);
|
||||
|
||||
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app.add_systems(ExtractSchedule, measure_allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Resource)]
|
||||
struct MeshAllocatorMeasurements {
|
||||
slabs: AtomicUsize,
|
||||
slabs_size: AtomicU64,
|
||||
allocations: AtomicUsize,
|
||||
}
|
||||
|
||||
fn add_mesh_allocator_measurement(
|
||||
mut diagnostics: Diagnostics,
|
||||
measurements: Res<MeshAllocatorMeasurements>,
|
||||
) {
|
||||
diagnostics.add_measurement(&MESH_ALLOCATOR_SLABS, || {
|
||||
measurements.slabs.load(Ordering::Relaxed) as f64
|
||||
});
|
||||
diagnostics.add_measurement(&MESH_ALLOCATOR_SLABS_SIZE, || {
|
||||
measurements.slabs_size.load(Ordering::Relaxed) as f64
|
||||
});
|
||||
diagnostics.add_measurement(&MESH_ALLOCATOR_ALLOCATIONS, || {
|
||||
measurements.allocations.load(Ordering::Relaxed) as f64
|
||||
});
|
||||
}
|
||||
|
||||
fn measure_allocator(
|
||||
measurements: Extract<Res<MeshAllocatorMeasurements>>,
|
||||
allocator: Res<MeshAllocator>,
|
||||
) {
|
||||
measurements
|
||||
.slabs
|
||||
.store(allocator.slab_count(), Ordering::Relaxed);
|
||||
measurements
|
||||
.slabs_size
|
||||
.store(allocator.slabs_size(), Ordering::Relaxed);
|
||||
measurements
|
||||
.allocations
|
||||
.store(allocator.allocations(), Ordering::Relaxed);
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
//! For more info, see [`RenderDiagnosticsPlugin`].
|
||||
|
||||
pub(crate) mod internal;
|
||||
mod mesh_allocator_diagnostic_plugin;
|
||||
mod render_asset_diagnostic_plugin;
|
||||
#[cfg(feature = "tracing-tracy")]
|
||||
mod tracy_gpu;
|
||||
|
||||
@ -16,6 +18,10 @@ use crate::{renderer::RenderAdapterInfo, RenderApp};
|
||||
use self::internal::{
|
||||
sync_diagnostics, DiagnosticsRecorder, Pass, RenderDiagnosticsMutex, WriteTimestamp,
|
||||
};
|
||||
pub use self::{
|
||||
mesh_allocator_diagnostic_plugin::MeshAllocatorDiagnosticPlugin,
|
||||
render_asset_diagnostic_plugin::RenderAssetDiagnosticPlugin,
|
||||
};
|
||||
|
||||
use super::{RenderDevice, RenderQueue};
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
use core::{any::type_name, marker::PhantomData};
|
||||
|
||||
use bevy_app::{Plugin, PreUpdate};
|
||||
use bevy_diagnostic::{Diagnostic, DiagnosticPath, Diagnostics, RegisterDiagnostic};
|
||||
use bevy_ecs::{resource::Resource, system::Res};
|
||||
use bevy_platform::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::{
|
||||
render_asset::{RenderAsset, RenderAssets},
|
||||
Extract, ExtractSchedule, RenderApp,
|
||||
};
|
||||
|
||||
pub struct RenderAssetDiagnosticPlugin<A: RenderAsset> {
|
||||
suffix: &'static str,
|
||||
_phantom: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: RenderAsset> RenderAssetDiagnosticPlugin<A> {
|
||||
pub fn new(suffix: &'static str) -> Self {
|
||||
Self {
|
||||
suffix,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_asset_diagnostic_path() -> DiagnosticPath {
|
||||
DiagnosticPath::from_components(["render_asset", type_name::<A>()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: RenderAsset> Plugin for RenderAssetDiagnosticPlugin<A> {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.register_diagnostic(
|
||||
Diagnostic::new(Self::render_asset_diagnostic_path()).with_suffix(self.suffix),
|
||||
)
|
||||
.init_resource::<RenderAssetMeasurements<A>>()
|
||||
.add_systems(PreUpdate, add_render_asset_measurement::<A>);
|
||||
|
||||
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app.add_systems(ExtractSchedule, measure_render_asset::<A>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Resource)]
|
||||
struct RenderAssetMeasurements<A: RenderAsset> {
|
||||
assets: AtomicUsize,
|
||||
_phantom: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: RenderAsset> Default for RenderAssetMeasurements<A> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
assets: AtomicUsize::default(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_render_asset_measurement<A: RenderAsset>(
|
||||
mut diagnostics: Diagnostics,
|
||||
measurements: Res<RenderAssetMeasurements<A>>,
|
||||
) {
|
||||
diagnostics.add_measurement(
|
||||
&RenderAssetDiagnosticPlugin::<A>::render_asset_diagnostic_path(),
|
||||
|| measurements.assets.load(Ordering::Relaxed) as f64,
|
||||
);
|
||||
}
|
||||
|
||||
fn measure_render_asset<A: RenderAsset>(
|
||||
measurements: Extract<Res<RenderAssetMeasurements<A>>>,
|
||||
assets: Res<RenderAssets<A>>,
|
||||
) {
|
||||
measurements
|
||||
.assets
|
||||
.store(assets.iter().count(), Ordering::Relaxed);
|
||||
}
|
@ -172,6 +172,15 @@ enum Slab {
|
||||
LargeObject(LargeObjectSlab),
|
||||
}
|
||||
|
||||
impl Slab {
|
||||
pub fn buffer_size(&self) -> u64 {
|
||||
match self {
|
||||
Self::General(gs) => gs.buffer.as_ref().map(|buffer| buffer.size()).unwrap_or(0),
|
||||
Self::LargeObject(lo) => lo.buffer.as_ref().map(|buffer| buffer.size()).unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A resizable slab that can contain multiple objects.
|
||||
///
|
||||
/// This is the normal type of slab used for objects that are below the
|
||||
@ -409,6 +418,20 @@ impl MeshAllocator {
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the number of allocated slabs
|
||||
pub fn slab_count(&self) -> usize {
|
||||
self.slabs.len()
|
||||
}
|
||||
|
||||
/// Get the total size of all allocated slabs
|
||||
pub fn slabs_size(&self) -> u64 {
|
||||
self.slabs.iter().map(|slab| slab.1.buffer_size()).sum()
|
||||
}
|
||||
|
||||
pub fn allocations(&self) -> usize {
|
||||
self.mesh_id_to_index_slab.len()
|
||||
}
|
||||
|
||||
/// Given a slab and a mesh with data located with it, returns the buffer
|
||||
/// and range of that mesh data within the slab.
|
||||
fn mesh_slice_in_slab(
|
||||
|
16
release-content/release-notes/render-assets-diagnostics.md
Normal file
16
release-content/release-notes/render-assets-diagnostics.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: Render Assets diagnostics
|
||||
authors: ["@hukasu"]
|
||||
pull_requests: [19311]
|
||||
---
|
||||
|
||||
## Goals
|
||||
|
||||
Create diagnostics plugins `MeshAllocatorDiagnosticPlugin`, `MaterialAllocatorDiagnosticPlugin` and `RenderAssetDiagnosticPlugin`
|
||||
that collect measurements related to `MeshAllocator`s, `MaterialBindGroupAllocator`, and `RenderAssets` respectively.
|
||||
|
||||
`MeshAllocatorDiagnosticPlugin` and `MaterialDiagnosticPlugin` measure the number of slabs, the total size of memory
|
||||
allocated by the slabs, and the number of objects allocated in the slabs. Only bindless materials use slabs for their
|
||||
allocations, non-bindless materials return 0 for all of them.
|
||||
|
||||
`RenderAssetDiagnosticsPlugin` measure the number of assets in `RenderAssets<T>`.
|
@ -13,12 +13,19 @@ impl Prepare for TestCommand {
|
||||
let jobs = args.build_jobs();
|
||||
let test_threads = args.test_threads();
|
||||
|
||||
let jobs_ref = &jobs;
|
||||
vec![PreparedCommand::new::<Self>(
|
||||
cmd!(
|
||||
sh,
|
||||
"cargo test --workspace --lib --bins --tests {no_fail_fast...} {jobs_ref...} -- {test_threads...}"
|
||||
),
|
||||
"Please fix failing tests in output above.",
|
||||
),PreparedCommand::new::<Self>(
|
||||
cmd!(
|
||||
sh,
|
||||
// `--benches` runs each benchmark once in order to verify that they behave
|
||||
// correctly and do not panic.
|
||||
"cargo test --workspace --lib --bins --tests --benches {no_fail_fast...} {jobs...} -- {test_threads...}"
|
||||
"cargo test --benches {no_fail_fast...} {jobs_ref...}"
|
||||
),
|
||||
"Please fix failing tests in output above.",
|
||||
)]
|
||||
|
Loading…
Reference in New Issue
Block a user