-
Notifications
You must be signed in to change notification settings - Fork 75
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
Alllow using custom GlobalProfiler instance with puffin_http::Server #212
Alllow using custom GlobalProfiler instance with puffin_http::Server #212
Conversation
- Add a new constructor function `Server::new_custom()` - Existing `Server::new()` now simply wraps this new function - Add extensive documentation including working examples
…stead for profiler handling
- Simplify the simple per-thread profiling example - Update macro example - Add dev-dependencies to make doc-tests pass for examples on `Server::new_custom()`
Example CodeI used a macro for this, but it generates something very similar to the following: pub mod main {
use std::sync::{Mutex, MutexGuard};
use once_cell::sync::Lazy;
use puffin_http::Server;
use puffin::{
FrameSink, FrameSinkId, StreamInfoRef, ScopeDetails,
ThreadProfiler, ThreadInfo, GlobalProfiler,
};
#[doc = concat!( "The address to bind the ", stringify!( main ), " thread profilers' server to" )]
pub const ADDR: &'static str = concat!( "127.0.0.1:", 8587 );
/// Installs the server's sink into the custom profiler
#[doc(hidden)]
fn install(sink: FrameSink) -> FrameSinkId {
self::lock().add_sink(sink)
}
/// Drops the server's sink and removes from profiler
#[doc(hidden)]
fn drop(id: FrameSinkId) {
self::lock().remove_sink(id);
}
#[doc = concat!( "The instance of the ", stringify!( main ), " thread profilers' server" )]
pub static SERVER: Lazy<Mutex<Server>> = Lazy::new(||
Mutex::new(
Server::new_custom(self::ADDR, self::install, self::drop).expect(&format!("{} puffin_http server failed to start", stringify!( main )))
)
});
#[doc = concat!( "A custom reporter for the ", std::stringify!( main ), " thread reporter" )]
pub fn reporter(info: ThreadInfo, details: &[ScopeDetails], stream: &StreamInfoRef<'_>) {
self::lock().report(info, details, stream)
}
#[doc = concat!( "Accessor for the ", stringify!( main ), " thread reporter" )]
pub fn lock() -> MutexGuard<'static, GlobalProfiler> {
static PROFILER: Lazy<Mutex<GlobalProfiler>> = Lazy::new(Default::default);
PROFILER.lock().expect(&format!("poisoned std::sync::mutex for {}", stringify!( main )))
}
#[doc = concat!( "Initialises the ", stringify!( main ), " thread reporter and server.\
Call this on each different thread you want to register with this profiler" )]
pub fn init_thread() {
std::mem::drop(self::SERVER.lock());
ThreadProfiler::initialize(::puffin::now_ns, self::reporter);
}
} Once I have the server objects generated (like above), I can use them like so: debug!(target: MAIN, "init puffin");
// Profiling is pretty low-cost
trace!(target: MAIN, "enable profiling");
puffin::set_scopes_on(true);
trace!(target: MAIN, "init main profiler");
profiler::main::init_thread();
// Special handling so the 'default' profiler passes on to our custom profiler
// In this case, we already overrode the ThreadProfiler for "main" using `main_profiler_init()`,
// So the events are already going to our custom profiler, but egui still calls `new_frame()` on the
// global profiler. So here, pass along the `new_frame()`s to the custom one
puffin::GlobalProfiler::lock().add_sink(Box::new(|_| profiler::main::lock().new_frame();)); This now redirects all the profiling events for whichever thread it was run in (in this case the UI thread), which get send to the |
@@ -127,7 +127,7 @@ impl GlobalProfiler { | |||
} | |||
|
|||
/// Reports some profiling data. Called from [`ThreadProfiler`]. | |||
pub(crate) fn report( | |||
pub fn report( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be made public so that GlobalProfiler::report()
can be called by our custom ThreadProfiler.reporter
that we assign.
@@ -25,3 +25,5 @@ puffin = { version = "0.19.0", path = "../puffin", features = [ | |||
|
|||
[dev-dependencies] | |||
simple_logger = "4.2" | |||
paste = "1.0.15" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These deps are here so that the example code can be tested in the docs for Server::new_custom()
.
We could modify the examples if needed to remove this, but it would be a decent amount of work
@emilk @TimonPost Any update on these? |
…ure/puffin_http_server_custom_profiler' Merge custom features branches, for use in rayna - EmbarkStudios devs haven't merged PR EmbarkStudios#212 EmbarkStudios#213 yet
Very cool! |
Checklist
Changes
Allow
puffin_http::Server
to use a custompuffin::GlobalProfiler
instance.Server::new_custom()
Server
as aSink
.GlobalProfiler
instance that isn't the default instance returned byGlobalProfiler::lock()
Server::new()
now simply wraps this new function around the defaultGlobalProfiler::lock()
function, so behaviour is the sameExample Usage
Here's a video of me using it to profile my project
rayna
.On the left we have my app, with the render profiler running top right and the ui profiler running bottom right. Note that this is one app process, but it has two separate
puffin_http::Server
instances. Note how in the video these profilers are sending frames completely independently, but for the same process.Apologies for the compression, it doesn't seem to like my noisy images...
2024-05-18_00-33-32.mp4