use crate::capability::{GpuRequirements, RenderCapability};
use crate::features::GpuFeatures;
impl RenderCapability for GpuFrameProfiler {
fn requirements() -> GpuRequirements {
GpuRequirements::new().request_features(
GpuFeatures::TIMESTAMP_QUERY
| GpuFeatures::TIMESTAMP_QUERY_INSIDE_ENCODERS
| GpuFeatures::TIMESTAMP_QUERY_INSIDE_PASSES,
)
}
fn name() -> &'static str {
"GpuFrameProfiler"
}
}
#[cfg(feature = "gpu-profiling")]
mod enabled {
use std::sync::{Arc, Mutex};
use crate::context::GraphicsContext;
use crate::features::GpuFeatures;
pub struct GpuFrameProfiler {
profiler: Mutex<wgpu_profiler::GpuProfiler>,
timestamp_period: f32,
has_timestamps: bool,
}
impl GpuFrameProfiler {
pub fn new(context: &Arc<GraphicsContext>) -> Result<Self, wgpu_profiler::CreationError> {
let has_timestamps = context.has_feature(GpuFeatures::TIMESTAMP_QUERY);
let has_encoder_timestamps =
context.has_feature(GpuFeatures::TIMESTAMP_QUERY_INSIDE_ENCODERS);
let has_pass_timestamps =
context.has_feature(GpuFeatures::TIMESTAMP_QUERY_INSIDE_PASSES);
if has_timestamps {
tracing::info!(
"GPU profiler: TIMESTAMP_QUERY=yes, INSIDE_ENCODERS={}, INSIDE_PASSES={}",
if has_encoder_timestamps { "yes" } else { "no" },
if has_pass_timestamps { "yes" } else { "no" },
);
if !has_encoder_timestamps {
tracing::warn!(
"GPU profiler: TIMESTAMP_QUERY_INSIDE_ENCODERS not available — \
scopes on command encoders will not produce timing data"
);
}
if !has_pass_timestamps {
tracing::warn!(
"GPU profiler: TIMESTAMP_QUERY_INSIDE_PASSES not available — \
scopes on render/compute passes will not produce timing data"
);
}
} else {
tracing::warn!(
"GPU profiler: TIMESTAMP_QUERY not enabled — debug groups only, no timing data. \
Use GraphicsContextDescriptor::request_capability::<GpuFrameProfiler>() to request it."
);
}
let profiler = wgpu_profiler::GpuProfiler::new(
context.device(),
wgpu_profiler::GpuProfilerSettings::default(),
)?;
let timestamp_period = context.queue().get_timestamp_period();
Ok(Self {
profiler: Mutex::new(profiler),
timestamp_period,
has_timestamps,
})
}
pub fn has_timestamp_queries(&self) -> bool {
self.has_timestamps
}
pub fn scope<'a, Recorder: wgpu_profiler::ProfilerCommandRecorder>(
&'a self,
label: impl Into<String>,
encoder_or_pass: &'a mut Recorder,
) -> GpuProfileScope<'a, Recorder> {
let profiler = self.profiler.lock().unwrap();
let profiler_ptr = &*profiler as *const wgpu_profiler::GpuProfiler;
let profiler_ref: &'a wgpu_profiler::GpuProfiler = unsafe { &*profiler_ptr };
let scope = profiler_ref.scope(label, encoder_or_pass);
GpuProfileScope {
scope,
_borrow: profiler,
}
}
pub fn resolve_queries(&self, encoder: &mut wgpu::CommandEncoder) {
self.profiler.lock().unwrap().resolve_queries(encoder);
}
pub fn end_frame(&self) -> Result<(), wgpu_profiler::EndFrameError> {
let mut profiler = self.profiler.lock().unwrap();
profiler.end_frame()?;
if let Some(results) = profiler.process_finished_frame(self.timestamp_period) {
wgpu_profiler::puffin::output_frame_to_puffin(
&mut puffin::GlobalProfiler::lock(),
&results,
);
}
Ok(())
}
pub fn inner(&self) -> &Mutex<wgpu_profiler::GpuProfiler> {
&self.profiler
}
}
pub struct GpuProfileScope<'a, Recorder: wgpu_profiler::ProfilerCommandRecorder> {
scope: wgpu_profiler::Scope<'a, Recorder>,
_borrow: std::sync::MutexGuard<'a, wgpu_profiler::GpuProfiler>,
}
impl<Recorder: wgpu_profiler::ProfilerCommandRecorder> std::ops::Deref
for GpuProfileScope<'_, Recorder>
{
type Target = Recorder;
fn deref(&self) -> &Self::Target {
&self.scope
}
}
impl<Recorder: wgpu_profiler::ProfilerCommandRecorder> std::ops::DerefMut
for GpuProfileScope<'_, Recorder>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.scope
}
}
}
#[cfg(feature = "gpu-profiling")]
pub use enabled::*;
#[cfg(not(feature = "gpu-profiling"))]
mod disabled {
use std::sync::Arc;
use crate::context::GraphicsContext;
pub struct GpuFrameProfiler;
impl GpuFrameProfiler {
pub fn new(_context: &Arc<GraphicsContext>) -> Result<Self, GpuFrameProfilerError> {
Ok(Self)
}
pub fn has_timestamp_queries(&self) -> bool {
false
}
pub fn resolve_queries(&self, _encoder: &mut wgpu::CommandEncoder) {}
pub fn end_frame(&self) -> Result<(), GpuFrameProfilerError> {
Ok(())
}
}
#[derive(Debug)]
pub struct GpuFrameProfilerError;
impl std::fmt::Display for GpuFrameProfilerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GPU profiling is disabled")
}
}
impl std::error::Error for GpuFrameProfilerError {}
}
#[cfg(not(feature = "gpu-profiling"))]
pub use disabled::*;
#[macro_export]
macro_rules! gpu_profile_scope {
($frame:expr, $label:expr, $body:expr) => {
$frame.with_gpu_scope($label, $body)
};
}