Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gpu profiler #3998

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ trace_chrome = ["trace", "bevy_internal/trace_chrome"]
trace_tracy = ["trace", "bevy_internal/trace_tracy"]
trace = ["bevy_internal/trace"]
wgpu_trace = ["bevy_internal/wgpu_trace"]
gpu_profiler = ["bevy_internal/gpu_profiler"]

# Image format support for texture loading (PNG and HDR are enabled by default)
hdr = ["bevy_internal/hdr"]
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ trace_chrome = [ "bevy_log/tracing-chrome" ]
trace_tracy = [ "bevy_log/tracing-tracy" ]
wgpu_trace = ["bevy_render/wgpu_trace"]
debug_asset_server = ["bevy_asset/debug_asset_server"]
gpu_profiler = [ "bevy_render/gpu_profiler" ]

# Image format support for texture loading (PNG and HDR are enabled by default)
hdr = ["bevy_render/hdr"]
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ trace = []
wgpu_trace = ["wgpu/trace"]
ci_limits = []
webgl = ["wgpu/webgl"]
gpu_profiler= [ "wgpu-profiler", "bevy_diagnostic" ]

[dependencies]
# bevy
Expand All @@ -33,6 +34,8 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.6.0", features = ["bevy"
bevy_transform = { path = "../bevy_transform", version = "0.6.0" }
bevy_window = { path = "../bevy_window", version = "0.6.0" }
bevy_utils = { path = "../bevy_utils", version = "0.6.0" }
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.6.0", optional=true }
wgpu-profiler = { version="0.8.0", optional = true }

# rendering
image = { version = "0.23.12", default-features = false }
Expand Down
81 changes: 81 additions & 0 deletions crates/bevy_render/src/gpu_profiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::sync::{Arc, Mutex, MutexGuard};

use bevy_diagnostic::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_ecs::system::{Res, ResMut};
use bevy_utils::HashMap;
use wgpu::{CommandEncoder, Queue};

use crate::renderer::RenderDevice;

pub struct GpuProfilerInner {
profiler: wgpu_profiler::GpuProfiler,
diagnostic_ids: HashMap<String, DiagnosticId>,
}

pub struct GpuProfileScope<'a>(MutexGuard<'a, GpuProfilerInner>);

impl<'a> GpuProfileScope<'a> {
pub fn end_scope(mut self, encoder: &mut CommandEncoder) {
self.0.profiler.end_scope(encoder);
}
}

#[derive(Clone)]
pub struct GpuProfiler(Arc<Mutex<GpuProfilerInner>>);

impl GpuProfiler {
pub fn new(queue: &Queue) -> Self {
let profiler = GpuProfiler(Arc::new(Mutex::new(GpuProfilerInner {
profiler: wgpu_profiler::GpuProfiler::new(4, queue.get_timestamp_period()),
diagnostic_ids: HashMap::default(),
})));
profiler
}
}

impl GpuProfiler {
pub fn begin_scope<'a>(
&'a self,
label: &str,
encoder: &mut CommandEncoder,
device: &RenderDevice,
) -> GpuProfileScope<'a> {
let mut scope = self.0.lock().unwrap();
scope
.profiler
.begin_scope(label, encoder, device.wgpu_device());
GpuProfileScope::<'a>(scope)
}

pub fn resolve_queries(&self, encoder: &mut CommandEncoder) {
let profiler = &mut self.0.lock().unwrap().profiler;
profiler.resolve_queries(encoder);
}

pub fn end_frame(&self, diagnostics: &mut Diagnostics) {
let profiler = &mut self.0.lock().unwrap();
profiler.profiler.end_frame().unwrap_or_default();
if let Some(results) = profiler.profiler.process_finished_frame() {
for result in results {
let id = match profiler.diagnostic_ids.get(&result.label) {
Some(id) => *id,
None => {
let diagnostic_id = DiagnosticId::default();
profiler
.diagnostic_ids
.insert(result.label.clone(), diagnostic_id);
let diagnostic =
Diagnostic::new(diagnostic_id, result.label, 600).with_suffix("ms");
diagnostics.add(diagnostic);
diagnostic_id
}
};
diagnostics.add_measurement(id, (result.time.end - result.time.start) * 1000.0);
}
}
}
}

pub fn gpu_profiler_system(profiler: Res<GpuProfiler>, mut diagnostics: ResMut<Diagnostics>) {
profiler.end_frame(diagnostics.as_mut());
}
13 changes: 12 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod camera;
pub mod color;
#[cfg(feature = "gpu_profiler")]
pub mod gpu_profiler;
pub mod mesh;
pub mod primitives;
pub mod render_asset;
Expand Down Expand Up @@ -41,6 +43,7 @@ use crate::{
texture::ImagePlugin,
view::{ViewPlugin, WindowRenderPlugin},
};

use bevy_app::{App, AppLabel, Plugin};
use bevy_asset::{AddAsset, AssetServer};
use bevy_ecs::prelude::*;
Expand Down Expand Up @@ -153,6 +156,15 @@ impl Plugin for RenderPlugin {
let mut render_app = App::empty();
let mut extract_stage =
SystemStage::parallel().with_system(RenderPipelineCache::extract_shaders);

#[cfg(feature = "gpu_profiler")]
{
let gpu_profiler = gpu_profiler::GpuProfiler::new(&queue);
app.insert_resource(gpu_profiler.clone());
render_app.insert_resource(gpu_profiler.clone());
extract_stage = extract_stage.with_system(gpu_profiler::gpu_profiler_system);
}

// don't apply buffers when the stage finishes running
// extract stage runs on the app world, but the buffers are applied to the render world
extract_stage.set_apply_buffers(false);
Expand All @@ -175,7 +187,6 @@ impl Plugin for RenderPlugin {
.insert_resource(render_pipeline_cache)
.insert_resource(asset_server)
.init_resource::<RenderGraph>();

app.add_sub_app(RenderApp, render_app, move |app_world, render_app| {
#[cfg(feature = "trace")]
let render_span = bevy_utils::tracing::info_span!("renderer subapp");
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_render/src/renderer/graph_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use std::ops::Deref;
use std::{borrow::Cow, collections::VecDeque};
use thiserror::Error;

#[cfg(feature = "gpu_profiler")]
use crate::gpu_profiler::GpuProfiler;
use crate::{
render_graph::{
Edge, NodeId, NodeRunError, NodeState, RenderGraph, RenderGraphContext, SlotLabel,
Expand Down Expand Up @@ -50,11 +52,15 @@ impl RenderGraphRunner {
queue: &wgpu::Queue,
world: &World,
) -> Result<(), RenderGraphRunnerError> {
#[cfg(feature = "gpu_profiler")]
let profiler = world.get_resource::<GpuProfiler>().unwrap();
let command_encoder =
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
let mut render_context = RenderContext {
render_device,
command_encoder,
#[cfg(feature = "gpu_profiler")]
profiler: profiler.clone(),
};

Self::run_graph(graph, None, &mut render_context, world, &[])?;
Expand All @@ -63,6 +69,8 @@ impl RenderGraphRunner {
let span = info_span!("submit_graph_commands");
#[cfg(feature = "trace")]
let _guard = span.enter();
#[cfg(feature = "gpu_profiler")]
profiler.resolve_queries(&mut render_context.command_encoder);
queue.submit(vec![render_context.command_encoder.finish()]);
}
Ok(())
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_render/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,6 @@ pub async fn initialize_renderer(
pub struct RenderContext {
pub render_device: RenderDevice,
pub command_encoder: CommandEncoder,
#[cfg(feature = "gpu_profiler")]
pub profiler: crate::gpu_profiler::GpuProfiler,
}
5 changes: 4 additions & 1 deletion crates/bevy_render/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,15 @@ impl Default for WgpuSettings {
limits
};

let features = wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
#[cfg(feature = "gpu_profiler")]
let features = features | wgpu::Features::TIMESTAMP_QUERY;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will fail on platforms that don't support timestamp queries. I don't know if I think that should be the preferred behaviour rather than silently not working.

Self {
device_label: Default::default(),
backends,
power_preference: PowerPreference::HighPerformance,
priority,
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
features,
disabled_features: None,
limits,
constrained_limits: None,
Expand Down