-
Notifications
You must be signed in to change notification settings - Fork 326
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
Integrate Ensogl stats with profiling framework #3388
Conversation
334456a
to
a78eb90
Compare
|
||
fn align_mark(mut mark: profiler_flame_graph::Mark, origin_x: f64) -> profiler_flame_graph::Mark { | ||
mark.position -= origin_x; | ||
mark.position += X_SCALE; |
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.
+
?
|
||
|
||
thread_local! { | ||
/// A common preamble used to start every shader program. |
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.
Comment from somewhere else?
@@ -146,6 +149,29 @@ impl FlameGraph { | |||
pub fn marks(&self) -> &[Mark] { | |||
&self.marks | |||
} | |||
|
|||
/// Add additional blocks to the visualisation. | |||
pub fn add_blocks<Blocks: Iterator<Item = profiler_flame_graph::Block>>( |
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.
API would be simpler with a add_block
interface here; caller can handle the looping
} | ||
|
||
/// Add additional marks to the visualisation. | ||
pub fn add_marks<Marks: Iterator<Item = profiler_flame_graph::Mark>>(&mut self, marks: Marks) { |
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.
Same as above
.iter_metadata() | ||
.filter(|metadata| matches!(metadata.data, Metadata::RenderStats(_))); | ||
for (prev, current) in render_stats.tuple_windows() { | ||
if let Metadata::RenderStats(data) = current.data { |
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.
The filter
/if let
seem redundant here; why not use a filter_map operation to get a correctly-type iterator?
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.
Maybe I'm missing something, but using filter_map
, I end up with something like
profile
.iter_metadata()
.filter(|metadata| matches!(metadata.data, Metadata::RenderStats(_)))
.tuple_windows()
.filter_map(|(prev, current)| {
if let Metadata::RenderStats(data) = current.data {
Some((prev, current, data))
} else {
None
}
})
.for_each(|(prev, current, data)| {
let start = prev.mark.into_ms();
let end = current.mark.into_ms();
let row = -1;
let label = format!("{:#?}", data);
let theme_color = match data.fps {
fps if fps > 55.0 => COLOR_FPS_GOOD,
fps if fps > 25.0 => COLOR_FPS_MEDIUM,
_ => COLOR_FPS_BAD,
};
let block = profiler_flame_graph::Block { start, end, row, label, theme_color };
blocks.push(block);
});
This doesn't seem really any better. Instead, we have a three tuple as an additional step. I don't see a good other way to retrieve the content of the Metadata Enum in some more elegant way, and we do need both previous, current and the data to generate the block.
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.
Could do this:
let render_stats = profile
.iter_metadata()
.filter_map(|metadata|
match metadata.data {
Metadata::RenderStats(data) => {
let mark = metadata.mark;
Some(enso_profiler_data::Metadata { mark, data })
},
_ => None,
});
for (prev, current) in render_stats.tuple_windows() {
let start = prev.mark.into_ms();
let end = current.mark.into_ms();
let row = -1;
let label = format!("{:#?}", current.data);
let block_type = match current.data.fps {
fps if fps > 55.0 => Performance::Good,
fps if fps > 25.0 => Performance::Medium,
_ => Performance::Bad,
};
let block = profiler_flame_graph::Block { start, end, row, label, block_type };
blocks.push(block);
}
I have named enso_profiler_data::Metadata
and its fields poorly; in a future PR I think I will rename it to Timestamped
with a time
field, so that usages like this would be more transparent.
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.
Neat! Didn't think of re-using the Metadata
with the inner data type. I'll do that its clearer.
pub state: State, | ||
pub label: String, | ||
/// Indicates the name of the color for lookup in the theme. | ||
pub theme_color: &'static str, |
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.
i liked the abstraction State
provided here--profiler_flame_graph::Block
was a high-level description of an element of a graph, and shape_from_block
lowered it to the ensogl
flame_graph::Block
, which was where the rendering details came in.
I see that a simple enum like State
no longer works now that different types of graph need to classify Block
s differently. I think although it would be a little more complex than this lowering-early approach, it might be worth introducing a parameterization like profiler_flame_graph::Block<BlockType>
(where ensogl
's flame_graph
contains something like impl From<SomeBlockType> for ThemeColor
for each concrete BlockType
) in order to maintain the clear boundary between the responsibilities of the two crates and their Block
types.
This would require fixing a bit of a hack from my original implementation of hybrid graph: There shouldn't be a hybrid graph type containing blocks arranged according to different disciplines; it would be better for the render-profile
scene to draw a callgraph, draw a rungraph under it, and draw the new 1-bar-high frametime graph in the stack too. I just did it the current way because it was easier due to my inexperience with Enso's FRP networks.
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.
Excellent explanation @kazcw! Thanks for writing it.
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 is an excellent approach! I made the changes. There are a few tweaks that deviate from your proposal, for example, not using From
due to orphan rule issues, but a new trait, which seemed nicer than a newtype to make the conversion work. But have a look and let me know if you think that can still be improved.
This would require fixing a bit of a hack from my original implementation of hybrid graph: There shouldn't be a hybrid graph type containing blocks arranged according to different disciplines; it would be better for the render-profile scene to draw a callgraph, draw a rungraph under it, and draw the new 1-bar-high frametime graph in the stack too. I just did it the current way because it was easier due to my inexperience with Enso's FRP networks.
Adding Sections
like this is on my agenda for the visualization, but I think here it is not yet needed. This change will probably come for the multiprocess visualization.
let label = format!("{:#?}", data); | ||
let theme_color = match data.fps { | ||
fps if fps > 55.0 => COLOR_FPS_GOOD, | ||
fps if fps > 25.0 => COLOR_FPS_MEDIUM, |
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.
I don't think "medium" is the right word between "good" and "bad". I cannot think of a good word now, but "medium" sounds off too me. To be confirmed with a native speaker.
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.
I changed the terminology a bit and instead of “FPS” I am using Performance
. For that, “Medium” seems to fit better.
pub state: State, | ||
pub label: String, | ||
/// Indicates the name of the color for lookup in the theme. | ||
pub theme_color: &'static str, |
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.
Excellent explanation @kazcw! Thanks for writing it.
.iter_metadata() | ||
.filter(|metadata| matches!(metadata.data, Metadata::RenderStats(_))); | ||
for (prev, current) in render_stats.tuple_windows() { | ||
if let Metadata::RenderStats(data) = current.data { |
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.
Could do this:
let render_stats = profile
.iter_metadata()
.filter_map(|metadata|
match metadata.data {
Metadata::RenderStats(data) => {
let mark = metadata.mark;
Some(enso_profiler_data::Metadata { mark, data })
},
_ => None,
});
for (prev, current) in render_stats.tuple_windows() {
let start = prev.mark.into_ms();
let end = current.mark.into_ms();
let row = -1;
let label = format!("{:#?}", current.data);
let block_type = match current.data.fps {
fps if fps > 55.0 => Performance::Good,
fps if fps > 25.0 => Performance::Medium,
_ => Performance::Bad,
};
let block = profiler_flame_graph::Block { start, end, row, label, block_type };
blocks.push(block);
}
I have named enso_profiler_data::Metadata
and its fields poorly; in a future PR I think I will rename it to Timestamped
with a time
field, so that usages like this would be more transparent.
Pull Request Description
Add logging of EnsoGL performance stats to the profiling framework. Also extends the visualization in the debug scene to show an overview of the performance stats. We now render a timeline of blocks that indicate by their colour the rough FPS range we are in:
Peek.2022-04-08.14-08.mp4
Important Notes
[ci no changelog needed]
Needs to be merged after #3382 as it requires some changes about metadata logging from there. That is why this PR is currently still in draft mode and based on that branch.
Checklist
Please include the following checklist in your PR:
./run dist
and./run watch
.