Introduce presentation metrics and send them to comet

This commit is contained in:
Héctor Ramón Jiménez 2025-04-09 21:50:21 +02:00
parent 6508ad67c1
commit 015f5283a8
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
8 changed files with 210 additions and 42 deletions

2
Cargo.lock generated
View file

@ -2556,6 +2556,7 @@ version = "0.14.0-dev"
dependencies = [
"bytemuck",
"cosmic-text",
"iced_debug",
"iced_graphics",
"kurbo 0.10.4",
"log",
@ -2575,6 +2576,7 @@ dependencies = [
"futures",
"glam",
"guillotiere",
"iced_debug",
"iced_graphics",
"log",
"lyon",

View file

@ -94,6 +94,7 @@ pub fn run() -> impl Stream<Item = Event> {
let mut last_message = String::new();
let mut last_commands_spawned = 0;
let mut last_present_window = None;
loop {
match receive(&mut stream, &mut buffer).await {
@ -146,6 +147,11 @@ pub fn run() -> impl Stream<Item = Event> {
last_message.clear();
last_commands_spawned = 0;
}
client::Event::SpanStarted(
span::Stage::Present(window),
) => {
last_present_window = Some(window);
}
client::Event::SpanStarted(_) => {}
client::Event::SpanFinished(
stage,
@ -173,6 +179,30 @@ pub fn run() -> impl Stream<Item = Event> {
span::Stage::Draw(window) => {
Span::Draw { window }
}
span::Stage::Prepare(primitive) => {
let Some(window) =
last_present_window
else {
continue;
};
Span::Prepare {
window,
primitive,
}
}
span::Stage::Render(primitive) => {
let Some(window) =
last_present_window
else {
continue;
};
Span::Render {
window,
primitive,
}
}
span::Stage::Present(window) => {
Span::Present { window }
}

View file

@ -21,6 +21,14 @@ pub enum Span {
Draw {
window: window::Id,
},
Prepare {
window: window::Id,
primitive: Primitive,
},
Render {
window: window::Id,
primitive: Primitive,
},
Present {
window: window::Id,
},
@ -30,6 +38,26 @@ pub enum Span {
},
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
)]
pub enum Primitive {
Quad,
Triangle,
Shader,
Text,
Image,
}
impl Span {
pub fn stage(&self) -> Stage {
match self {
@ -39,6 +67,8 @@ impl Span {
Span::Layout { window } => Stage::Layout(*window),
Span::Interact { window } => Stage::Interact(*window),
Span::Draw { window } => Stage::Draw(*window),
Span::Prepare { primitive, .. } => Stage::Prepare(*primitive),
Span::Render { primitive, .. } => Stage::Render(*primitive),
Span::Present { window } => Stage::Present(*window),
Span::Custom { window, name } => {
Stage::Custom(*window, name.clone())
@ -58,6 +88,8 @@ pub enum Stage {
Interact(window::Id),
Draw(window::Id),
Present(window::Id),
Prepare(Primitive),
Render(Primitive),
Custom(window::Id, String),
}
@ -70,6 +102,8 @@ impl std::fmt::Display for Stage {
Stage::Layout(_) => "Layout",
Stage::Interact(_) => "Interact",
Stage::Draw(_) => "Draw",
Stage::Prepare(_) => "Prepare",
Stage::Render(_) => "Render",
Stage::Present(_) => "Present",
Stage::Custom(_, name) => name,
})

View file

@ -7,6 +7,15 @@ pub use internal::Span;
use std::io;
#[derive(Debug, Clone, Copy)]
pub enum Primitive {
Quad,
Triangle,
Shader,
Image,
Text,
}
pub fn init(name: &str) {
internal::init(name);
}
@ -51,6 +60,14 @@ pub fn draw(window: window::Id) -> Span {
internal::draw(window)
}
pub fn prepare(primitive: Primitive) -> Span {
internal::prepare(primitive)
}
pub fn render(primitive: Primitive) -> Span {
internal::render(primitive)
}
pub fn present(window: window::Id) -> Span {
internal::present(window)
}
@ -65,6 +82,7 @@ pub fn skip_next_timing() {
#[cfg(all(feature = "enable", not(target_arch = "wasm32")))]
mod internal {
use crate::Primitive;
use crate::core::theme;
use crate::core::time::Instant;
use crate::core::window;
@ -171,6 +189,14 @@ mod internal {
span(span::Stage::Draw(window))
}
pub fn prepare(primitive: Primitive) -> Span {
span(span::Stage::Prepare(to_primitive(primitive)))
}
pub fn render(primitive: Primitive) -> Span {
span(span::Stage::Render(to_primitive(primitive)))
}
pub fn present(window: window::Id) -> Span {
span(span::Stage::Present(window))
}
@ -192,6 +218,16 @@ mod internal {
}
}
fn to_primitive(primitive: Primitive) -> span::Primitive {
match primitive {
Primitive::Quad => span::Primitive::Quad,
Primitive::Triangle => span::Primitive::Triangle,
Primitive::Shader => span::Primitive::Shader,
Primitive::Text => span::Primitive::Text,
Primitive::Image => span::Primitive::Image,
}
}
#[derive(Debug)]
pub struct Span {
span: span::Stage,
@ -222,6 +258,7 @@ mod internal {
#[cfg(any(not(feature = "enable"), target_arch = "wasm32"))]
mod internal {
use crate::Primitive;
use crate::core::theme;
use crate::core::window;
@ -263,6 +300,14 @@ mod internal {
Span
}
pub fn prepare(_primitive: Primitive) -> Span {
Span
}
pub fn render(_primitive: Primitive) -> Span {
Span
}
pub fn present(_window: window::Id) -> Span {
Span
}

View file

@ -19,6 +19,7 @@ svg = ["iced_graphics/svg", "resvg"]
geometry = ["iced_graphics/geometry"]
[dependencies]
iced_debug.workspace = true
iced_graphics.workspace = true
bytemuck.workspace = true

View file

@ -17,6 +17,7 @@ mod vector;
#[cfg(feature = "geometry")]
pub mod geometry;
use iced_debug as debug;
pub use iced_graphics as graphics;
pub use iced_graphics::core;
@ -112,62 +113,84 @@ impl Renderer {
engine::adjust_clip_mask(clip_mask, clip_bounds);
for (quad, background) in &layer.quads {
self.engine.draw_quad(
quad,
background,
Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
if !layer.quads.is_empty() {
let render_span = debug::render(debug::Primitive::Quad);
for (quad, background) in &layer.quads {
self.engine.draw_quad(
quad,
background,
Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
}
render_span.finish();
}
for group in &layer.primitives {
let Some(new_clip_bounds) = (group.clip_bounds()
* scale_factor)
.intersection(&clip_bounds)
else {
continue;
};
if !layer.primitives.is_empty() {
let render_span = debug::render(debug::Primitive::Triangle);
engine::adjust_clip_mask(clip_mask, new_clip_bounds);
for group in &layer.primitives {
let Some(new_clip_bounds) = (group.clip_bounds()
* scale_factor)
.intersection(&clip_bounds)
else {
continue;
};
for primitive in group.as_slice() {
self.engine.draw_primitive(
primitive,
group.transformation()
* Transformation::scale(scale_factor),
engine::adjust_clip_mask(clip_mask, new_clip_bounds);
for primitive in group.as_slice() {
self.engine.draw_primitive(
primitive,
group.transformation()
* Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
}
engine::adjust_clip_mask(clip_mask, clip_bounds);
}
render_span.finish();
}
if !layer.images.is_empty() {
let render_span = debug::render(debug::Primitive::Image);
for image in &layer.images {
self.engine.draw_image(
image,
Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
}
engine::adjust_clip_mask(clip_mask, clip_bounds);
render_span.finish();
}
for image in &layer.images {
self.engine.draw_image(
image,
Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
}
if !layer.text.is_empty() {
let render_span = debug::render(debug::Primitive::Image);
for group in &layer.text {
for text in group.as_slice() {
self.engine.draw_text(
text,
group.transformation()
* Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
for group in &layer.text {
for text in group.as_slice() {
self.engine.draw_text(
text,
group.transformation()
* Transformation::scale(scale_factor),
pixels,
clip_mask,
clip_bounds,
);
}
}
render_span.finish();
}
}
}

View file

@ -26,6 +26,7 @@ webgl = ["wgpu/webgl"]
strict-assertions = []
[dependencies]
iced_debug.workspace = true
iced_graphics.workspace = true
bitflags.workspace = true

View file

@ -47,6 +47,7 @@ mod image;
use buffer::Buffer;
use iced_debug as debug;
pub use iced_graphics as graphics;
pub use iced_graphics::core;
@ -320,6 +321,8 @@ impl Renderer {
}
if !layer.quads.is_empty() {
let prepare_span = debug::prepare(debug::Primitive::Quad);
self.quad.prepare(
&self.engine.quad_pipeline,
&self.engine.device,
@ -329,9 +332,13 @@ impl Renderer {
viewport.projection(),
scale_factor,
);
prepare_span.finish();
}
if !layer.triangles.is_empty() {
let prepare_span = debug::prepare(debug::Primitive::Triangle);
self.triangle.prepare(
&self.engine.triangle_pipeline,
&self.engine.device,
@ -341,9 +348,13 @@ impl Renderer {
Transformation::scale(scale_factor),
viewport.physical_size(),
);
prepare_span.finish();
}
if !layer.primitives.is_empty() {
let prepare_span = debug::prepare(debug::Primitive::Shader);
let mut primitive_storage = self
.engine
.primitive_storage
@ -360,10 +371,14 @@ impl Renderer {
viewport,
);
}
prepare_span.finish();
}
#[cfg(any(feature = "svg", feature = "image"))]
if !layer.images.is_empty() {
let prepare_span = debug::prepare(debug::Primitive::Image);
self.image.prepare(
&self.engine.image_pipeline,
&self.engine.device,
@ -374,9 +389,13 @@ impl Renderer {
viewport.projection(),
scale_factor,
);
prepare_span.finish();
}
if !layer.text.is_empty() {
let prepare_span = debug::prepare(debug::Primitive::Text);
self.text.prepare(
&self.engine.text_pipeline,
&self.engine.device,
@ -387,6 +406,8 @@ impl Renderer {
layer.bounds,
Transformation::scale(scale_factor),
);
prepare_span.finish();
}
}
}
@ -459,6 +480,7 @@ impl Renderer {
};
if !layer.quads.is_empty() {
let render_span = debug::render(debug::Primitive::Quad);
self.quad.render(
&self.engine.quad_pipeline,
quad_layer,
@ -466,6 +488,7 @@ impl Renderer {
&layer.quads,
&mut render_pass,
);
render_span.finish();
quad_layer += 1;
}
@ -473,6 +496,7 @@ impl Renderer {
if !layer.triangles.is_empty() {
let _ = ManuallyDrop::into_inner(render_pass);
let render_span = debug::render(debug::Primitive::Triangle);
mesh_layer += self.triangle.render(
&self.engine.triangle_pipeline,
encoder,
@ -482,6 +506,7 @@ impl Renderer {
physical_bounds,
scale,
);
render_span.finish();
render_pass = ManuallyDrop::new(encoder.begin_render_pass(
&wgpu::RenderPassDescriptor {
@ -504,6 +529,7 @@ impl Renderer {
}
if !layer.primitives.is_empty() {
let render_span = debug::render(debug::Primitive::Shader);
let _ = ManuallyDrop::into_inner(render_pass);
let primitive_storage = self
@ -526,6 +552,8 @@ impl Renderer {
}
}
render_span.finish();
render_pass = ManuallyDrop::new(encoder.begin_render_pass(
&wgpu::RenderPassDescriptor {
label: Some("iced_wgpu render pass"),
@ -548,6 +576,7 @@ impl Renderer {
#[cfg(any(feature = "svg", feature = "image"))]
if !layer.images.is_empty() {
let render_span = debug::render(debug::Primitive::Image);
self.image.render(
&self.engine.image_pipeline,
&image_cache,
@ -555,11 +584,13 @@ impl Renderer {
scissor_rect,
&mut render_pass,
);
render_span.finish();
image_layer += 1;
}
if !layer.text.is_empty() {
let render_span = debug::render(debug::Primitive::Text);
text_layer += self.text.render(
&self.engine.text_pipeline,
&self.text_viewport,
@ -568,6 +599,7 @@ impl Renderer {
scissor_rect,
&mut render_pass,
);
render_span.finish();
}
}