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

add trait extension in addition to raw implementations on scopes #78

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
76 changes: 70 additions & 6 deletions examples/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,36 @@ impl ApplicationHandler<()> for State {
WindowEvent::RedrawRequested => {
profiling::scope!("Redraw Requested");

let frame = surface
.get_current_texture()
.expect("Failed to acquire next surface texture");
let mut frame;
Copy link
Owner

Choose a reason for hiding this comment

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

that's nice but also out of place for this PR
Any reason it does 10x retry without pumping the window loop, has this been observed to be actually getting around issues? (I'm curious!)

Copy link
Author

Choose a reason for hiding this comment

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

It failed to acquire sometimes. 10 retires is arbitrary but fixed it for me - Debian 12, wayland.

Have done it this way in other winit applications I have been working on and it seems to work

let mut i = 0;
loop {
i += 1;
frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(err) => match err {
wgpu::SurfaceError::Timeout => {
if i > 10 {
return;
}
continue;
}

wgpu::SurfaceError::Outdated => {
if i > 10 {
return;
}
surface.configure(&device, surface_desc);
continue;
}

wgpu::SurfaceError::Lost | wgpu::SurfaceError::OutOfMemory => {
panic!("Failed to acquire next surface texture: {err}");
}
},
};
break;
}

let frame_view = frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
Expand Down Expand Up @@ -334,6 +361,18 @@ fn draw(
rpass.draw(0..6, 1..2);
}
}

{
// You can also use traits to create your own helper functions on your scopes
let mut rpass = my_fancy_pass(&mut scope, device, view);
rpass.set_pipeline(render_pipeline);

{
let mut rpass = rpass.scope("fractal 2", device);
rpass.draw(0..6, 2..3);
};
}

{
// It's also possible to take timings by hand, manually calling `begin_query` and `end_query`.
// This is generally not recommended as it's very easy to mess up by accident :)
Expand Down Expand Up @@ -363,17 +402,17 @@ fn draw(
// Again, to do any actual timing, you need to enable wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES.
{
let query = profiler
.begin_query("fractal 2", &mut rpass, device)
.begin_query("fractal 3", &mut rpass, device)
.with_parent(Some(&pass_scope));
rpass.draw(0..6, 2..3);
rpass.draw(0..6, 3..4);

// Don't forget to end the query!
profiler.end_query(&mut rpass, query);
}
// Another variant is to use `ManualOwningScope`, forming a middle ground between no scope helpers and fully automatic scope closing.
let mut rpass = {
let mut rpass = profiler.manual_owning_scope("fractal 3", rpass, device);
rpass.draw(0..6, 3..4);
rpass.draw(0..6, 4..5);

// Don't forget to end the scope.
// Ending a `ManualOwningScope` will return the pass or encoder it owned.
Expand All @@ -385,6 +424,31 @@ fn draw(
}
}

pub fn my_fancy_pass<'b, E: wgpu_profiler::PassEncoderExt>(
e: &'b mut E,
device: &wgpu::Device,
view: &wgpu::TextureView,
) -> wgpu_profiler::OwningScope<'b, wgpu::RenderPass<'b>> {
e.scoped_render_pass(
"My Fancy Pass!",
device,
wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
..Default::default()
},
)
}

fn main() {
tracy_client::Client::start();
//env_logger::init_from_env(env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "warn"));
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,4 @@ pub use profiler::GpuProfiler;
pub use profiler_command_recorder::ProfilerCommandRecorder;
pub use profiler_query::{GpuProfilerQuery, GpuTimerQueryResult};
pub use profiler_settings::GpuProfilerSettings;
pub use scope::{ManualOwningScope, OwningScope, Scope};
pub use scope::{ManualOwningScope, OwningScope, PassEncoderExt, Scope, ScopeExt};
58 changes: 58 additions & 0 deletions src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,36 @@ impl<'a, R: ProfilerCommandRecorder> ManualOwningScope<'a, R> {
}
}

pub trait ScopeExt<R: ProfilerCommandRecorder>:
std::ops::Deref<Target = R> + std::ops::DerefMut<Target = R>
{
/// Starts a new profiler scope nested within this one.
#[must_use]
#[track_caller]
fn scope(&mut self, label: impl Into<String>, device: &wgpu::Device) -> Scope<'_, R>;
}

pub trait PassEncoderExt:
std::ops::Deref<Target = wgpu::CommandEncoder>
+ std::ops::DerefMut<Target = wgpu::CommandEncoder>
+ ScopeExt<wgpu::CommandEncoder>
{
#[track_caller]
fn scoped_render_pass<'a>(
&'a mut self,
label: impl Into<String>,
device: &wgpu::Device,
pass_descriptor: wgpu::RenderPassDescriptor<'_>,
) -> OwningScope<'a, wgpu::RenderPass<'a>>;

#[track_caller]
fn scoped_compute_pass<'a>(
&'a mut self,
label: impl Into<String>,
device: &wgpu::Device,
) -> OwningScope<'a, wgpu::ComputePass<'a>>;
}

/// Most implementation code of the different scope types is exactly the same.
///
/// This macro allows to avoid code duplication.
Expand Down Expand Up @@ -93,6 +123,13 @@ macro_rules! impl_scope_ext {
}
}

impl<'a, R: ProfilerCommandRecorder> ScopeExt<R> for $scope<'a, R> {
#[inline(always)]
fn scope(&mut self, label: impl Into<String>, device: &wgpu::Device) -> Scope<'_, R> {
$scope::scope(self, label, device)
}
}

impl<'a> $scope<'a, wgpu::CommandEncoder> {
/// Start a render pass wrapped in a [`OwningScope`].
///
Expand Down Expand Up @@ -159,6 +196,27 @@ macro_rules! impl_scope_ext {
}
}

impl PassEncoderExt for $scope<'_, wgpu::CommandEncoder> {
#[track_caller]
fn scoped_render_pass<'a>(
&'a mut self,
label: impl Into<String>,
device: &wgpu::Device,
pass_descriptor: wgpu::RenderPassDescriptor<'_>,
) -> OwningScope<'a, wgpu::RenderPass<'a>> {
$scope::scoped_render_pass(self, label, device, pass_descriptor)
}

#[track_caller]
fn scoped_compute_pass<'a>(
&'a mut self,
label: impl Into<String>,
device: &wgpu::Device,
) -> OwningScope<'a, wgpu::ComputePass<'a>> {
$scope::scoped_compute_pass(self, label, device)
}
}

impl<'a, R: ProfilerCommandRecorder> std::ops::Deref for $scope<'a, R> {
type Target = R;

Expand Down