diff --git a/CHANGELOG.md b/CHANGELOG.md index 955625cc..4705a261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Backend: pointer (mouse/touch) position and buttons are now passed to egui in the event stream. * `DragValue::range` is now called `clamp_range` and also clamps incoming values. * Renamed `Triangles` to `Mesh`. +* The tesselator now wraps the clip rectangle and mesh in `struct ClippedMesh(Rect, Mesh)`. ### Fixed 🐛 diff --git a/README.md b/README.md index c133cba2..68183fde 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ The same code can be compiled to a native app or a web app. ### Writing your own egui integration -You need to collect [`egui::RawInput`](https://docs.rs/egui/latest/egui/struct.RawInput.html), paint [`egui::PaintJobs`](https://docs.rs/egui/latest/egui/paint/tessellator/type.PaintJobs.html) and handle [`egui::Output`](https://docs.rs/egui/latest/egui/struct.Output.html). The basic structure is this: +You need to collect [`egui::RawInput`](https://docs.rs/egui/latest/egui/struct.RawInput.html), paint [`egui::ClippedMesh`](https://docs.rs/epaint/):es and handle [`egui::Output`](https://docs.rs/egui/latest/egui/struct.Output.html). The basic structure is this: ``` rust let mut egui_ctx = egui::Context::new(); @@ -179,8 +179,8 @@ loop { egui_ctx.begin_frame(raw_input); my_app.ui(&mut egui_ctx); // add panels, windows and widgets to `egui_ctx` here let (output, shapes) = egui_ctx.end_frame(); - let paint_jobs = egui_ctx.tessellate(shapes); // create triangles to paint - my_integration.paint(paint_jobs); + let clipped_meshes = egui_ctx.tessellate(shapes); // create triangles to paint + my_integration.paint(clipped_meshes); my_integration.set_cursor_icon(output.cursor_icon); // Also see `egui::Output` for more } diff --git a/egui/src/context.rs b/egui/src/context.rs index 5d8d6068..fd4ffe34 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -583,13 +583,14 @@ impl Context { } /// Tessellate the given shapes into triangle meshes. - pub fn tessellate(&self, shapes: Vec) -> PaintJobs { + pub fn tessellate(&self, shapes: Vec) -> Vec { let mut tessellation_options = self.memory().options.tessellation_options; tessellation_options.aa_size = 1.0 / self.pixels_per_point(); let paint_stats = PaintStats::from_shapes(&shapes); // TODO: internal allocations - let paint_jobs = tessellator::tessellate_shapes(shapes, tessellation_options, self.fonts()); - *self.paint_stats.lock() = paint_stats.with_paint_jobs(&paint_jobs); - paint_jobs + let clipped_meshes = + tessellator::tessellate_shapes(shapes, tessellation_options, self.fonts()); + *self.paint_stats.lock() = paint_stats.with_clipped_meshes(&clipped_meshes); + clipped_meshes } // --------------------------------------------------------------------- diff --git a/egui/src/introspection.rs b/egui/src/introspection.rs index 0ca16a29..54817c31 100644 --- a/egui/src/introspection.rs +++ b/egui/src/introspection.rs @@ -88,7 +88,7 @@ impl Widget for &epaint::stats::PaintStats { shape_path, shape_mesh, shape_vec, - jobs, + clipped_meshes, vertices, indices, } = self; @@ -97,12 +97,13 @@ impl Widget for &epaint::stats::PaintStats { label(ui, shapes, "shapes").on_hover_text("Boxes, circles, etc"); label(ui, shape_text, "text"); label(ui, shape_path, "paths"); - label(ui, shape_mesh, "meshes"); - label(ui, shape_vec, "nested"); + label(ui, shape_mesh, "nested meshes"); + label(ui, shape_vec, "nested shapes"); ui.advance_cursor(10.0); ui.label("Tessellated:"); - label(ui, jobs, "jobs").on_hover_text("Number of separate clip rectangles"); + label(ui, clipped_meshes, "clipped_meshes") + .on_hover_text("Number of separate clip rectangles"); label(ui, vertices, "vertices"); label(ui, indices, "indices").on_hover_text("Three 32-bit indices per triangles"); ui.advance_cursor(10.0); diff --git a/egui/src/lib.rs b/egui/src/lib.rs index 5c1019d0..cc2a9389 100644 --- a/egui/src/lib.rs +++ b/egui/src/lib.rs @@ -27,8 +27,8 @@ //! egui_ctx.begin_frame(raw_input); //! my_app.ui(&egui_ctx); // add panels, windows and widgets to `egui_ctx` here //! let (output, shapes) = egui_ctx.end_frame(); -//! let paint_jobs = egui_ctx.tessellate(shapes); // create triangles to paint -//! my_integration.paint(paint_jobs); +//! let clipped_meshes = egui_ctx.tessellate(shapes); // create triangles to paint +//! my_integration.paint(clipped_meshes); //! my_integration.set_cursor_icon(output.cursor_icon); //! // Also see `egui::Output` for more //! } @@ -109,7 +109,7 @@ pub use emath::{ pub use epaint::{ color, mutex, text::{FontDefinitions, FontFamily, TextStyle}, - Color32, PaintJobs, Rgba, Shape, Stroke, Texture, TextureId, + ClippedMesh, Color32, Rgba, Shape, Stroke, Texture, TextureId, }; pub use { diff --git a/egui_demo_lib/src/lib.rs b/egui_demo_lib/src/lib.rs index ecd3abae..db17fa4d 100644 --- a/egui_demo_lib/src/lib.rs +++ b/egui_demo_lib/src/lib.rs @@ -100,7 +100,7 @@ fn test_egui_e2e() { ctx.begin_frame(raw_input.clone()); demo_windows.ui(&ctx); let (_output, shapes) = ctx.end_frame(); - let paint_jobs = ctx.tessellate(shapes); - assert!(!paint_jobs.is_empty()); + let clipped_meshes = ctx.tessellate(shapes); + assert!(!clipped_meshes.is_empty()); } } diff --git a/egui_glium/src/backend.rs b/egui_glium/src/backend.rs index 192a23e5..c87c555a 100644 --- a/egui_glium/src/backend.rs +++ b/egui_glium/src/backend.rs @@ -212,7 +212,7 @@ pub fn run(mut app: Box) -> ! { .build(); app.update(&ctx, &mut frame); let (egui_output, shapes) = ctx.end_frame(); - let paint_jobs = ctx.tessellate(shapes); + let clipped_meshes = ctx.tessellate(shapes); let frame_time = (Instant::now() - frame_start).as_secs_f64() as f32; previous_frame_time = Some(frame_time); @@ -220,7 +220,7 @@ pub fn run(mut app: Box) -> ! { &display, ctx.pixels_per_point(), app.clear_color(), - paint_jobs, + clipped_meshes, &ctx.texture(), ); diff --git a/egui_glium/src/painter.rs b/egui_glium/src/painter.rs index ad2b0a5a..b55508d1 100644 --- a/egui_glium/src/painter.rs +++ b/egui_glium/src/painter.rs @@ -1,11 +1,7 @@ #![allow(deprecated)] // legacy implement_vertex macro use { - egui::{ - math::clamp, - paint::{Mesh, PaintJobs}, - Color32, Rect, - }, + egui::{math::clamp, paint::Mesh, Color32, Rect}, glium::{ implement_vertex, index::PrimitiveType, @@ -128,7 +124,7 @@ impl Painter { display: &glium::Display, pixels_per_point: f32, clear_color: egui::Rgba, - jobs: PaintJobs, + cipped_meshes: Vec, egui_texture: &egui::Texture, ) { self.upload_egui_texture(display, egui_texture); @@ -142,7 +138,7 @@ impl Painter { clear_color[2], clear_color[3], ); - for (clip_rect, mesh) in jobs { + for egui::ClippedMesh(clip_rect, mesh) in cipped_meshes { self.paint_mesh(&mut target, display, pixels_per_point, clip_rect, &mesh) } target.finish().unwrap(); diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index d155f9cd..612e097f 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -42,30 +42,30 @@ impl WebBackend { self.ctx.begin_frame(raw_input) } - pub fn end_frame(&mut self) -> Result<(egui::Output, egui::PaintJobs), JsValue> { + pub fn end_frame(&mut self) -> Result<(egui::Output, Vec), JsValue> { let frame_start = self .frame_start .take() .expect("unmatched calls to begin_frame/end_frame"); let (output, shapes) = self.ctx.end_frame(); - let paint_jobs = self.ctx.tessellate(shapes); + let clipped_meshes = self.ctx.tessellate(shapes); let now = now_sec(); self.previous_frame_time = Some((now - frame_start) as f32); - Ok((output, paint_jobs)) + Ok((output, clipped_meshes)) } pub fn paint( &mut self, clear_color: egui::Rgba, - paint_jobs: egui::PaintJobs, + clipped_meshes: Vec, ) -> Result<(), JsValue> { self.painter.upload_egui_texture(&self.ctx.texture()); self.painter.clear(clear_color); self.painter - .paint_meshes(paint_jobs, self.ctx.pixels_per_point()) + .paint_meshes(clipped_meshes, self.ctx.pixels_per_point()) } pub fn painter_debug_info(&self) -> String { @@ -190,7 +190,7 @@ impl AppRunner { Ok(()) } - pub fn logic(&mut self) -> Result<(egui::Output, egui::PaintJobs), JsValue> { + pub fn logic(&mut self) -> Result<(egui::Output, Vec), JsValue> { resize_canvas_to_screen_size(self.web_backend.canvas_id()); let canvas_size = canvas_size_in_points(self.web_backend.canvas_id()); let raw_input = self.input.new_frame(canvas_size); @@ -216,7 +216,7 @@ impl AppRunner { let egui_ctx = &self.web_backend.ctx; self.app.update(egui_ctx, &mut frame); - let (egui_output, paint_jobs) = self.web_backend.end_frame()?; + let (egui_output, clipped_meshes) = self.web_backend.end_frame()?; handle_output(&egui_output); { @@ -227,11 +227,12 @@ impl AppRunner { } = app_output; } - Ok((egui_output, paint_jobs)) + Ok((egui_output, clipped_meshes)) } - pub fn paint(&mut self, paint_jobs: egui::PaintJobs) -> Result<(), JsValue> { - self.web_backend.paint(self.app.clear_color(), paint_jobs) + pub fn paint(&mut self, clipped_meshes: Vec) -> Result<(), JsValue> { + self.web_backend + .paint(self.app.clear_color(), clipped_meshes) } } diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index 9d48cbe3..1ea9dd69 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -405,8 +405,8 @@ fn paint_and_schedule(runner_ref: AppRunnerRef) -> Result<(), JsValue> { fn paint_if_needed(runner_ref: &AppRunnerRef) -> Result<(), JsValue> { let mut runner_lock = runner_ref.0.lock(); if runner_lock.needs_repaint.fetch_and_clear() { - let (output, paint_jobs) = runner_lock.logic()?; - runner_lock.paint(paint_jobs)?; + let (output, clipped_meshes) = runner_lock.logic()?; + runner_lock.paint(clipped_meshes)?; if output.needs_repaint { runner_lock.needs_repaint.set_true(); } diff --git a/egui_web/src/painter.rs b/egui_web/src/painter.rs index b5a1aa1f..fa1d84b2 100644 --- a/egui_web/src/painter.rs +++ b/egui_web/src/painter.rs @@ -10,8 +10,11 @@ pub trait Painter { fn upload_egui_texture(&mut self, texture: &egui::Texture); - fn clear(&mut self, lear_color: egui::Rgba); + fn clear(&mut self, clear_color: egui::Rgba); - fn paint_meshes(&mut self, jobs: egui::PaintJobs, pixels_per_point: f32) - -> Result<(), JsValue>; + fn paint_meshes( + &mut self, + clipped_meshes: Vec, + pixels_per_point: f32, + ) -> Result<(), JsValue>; } diff --git a/egui_web/src/webgl1.rs b/egui_web/src/webgl1.rs index b3e26710..b4b61ff1 100644 --- a/egui_web/src/webgl1.rs +++ b/egui_web/src/webgl1.rs @@ -6,7 +6,7 @@ use { use egui::{ math::clamp, - paint::{Color32, Mesh, PaintJobs, Texture}, + paint::{Color32, Mesh, Texture}, vec2, }; @@ -478,7 +478,11 @@ impl crate::Painter for WebGlPainter { gl.clear(Gl::COLOR_BUFFER_BIT); } - fn paint_meshes(&mut self, jobs: PaintJobs, pixels_per_point: f32) -> Result<(), JsValue> { + fn paint_meshes( + &mut self, + clipped_meshes: Vec, + pixels_per_point: f32, + ) -> Result<(), JsValue> { self.upload_user_textures(); let gl = &self.gl; @@ -504,7 +508,7 @@ impl crate::Painter for WebGlPainter { let u_sampler_loc = gl.get_uniform_location(&self.program, "u_sampler").unwrap(); gl.uniform1i(Some(&u_sampler_loc), 0); - for (clip_rect, mesh) in jobs { + for egui::ClippedMesh(clip_rect, mesh) in clipped_meshes { if let Some(gl_texture) = self.get_texture(mesh.texture_id) { gl.bind_texture(Gl::TEXTURE_2D, Some(gl_texture)); diff --git a/egui_web/src/webgl2.rs b/egui_web/src/webgl2.rs index 17930855..92faf208 100644 --- a/egui_web/src/webgl2.rs +++ b/egui_web/src/webgl2.rs @@ -8,7 +8,7 @@ use { use egui::{ math::clamp, - paint::{Color32, Mesh, PaintJobs, Texture}, + paint::{Color32, Mesh, Texture}, vec2, }; @@ -467,7 +467,11 @@ impl crate::Painter for WebGl2Painter { gl.clear(Gl::COLOR_BUFFER_BIT); } - fn paint_meshes(&mut self, jobs: PaintJobs, pixels_per_point: f32) -> Result<(), JsValue> { + fn paint_meshes( + &mut self, + clipped_meshes: Vec, + pixels_per_point: f32, + ) -> Result<(), JsValue> { self.upload_user_textures(); let gl = &self.gl; @@ -493,7 +497,7 @@ impl crate::Painter for WebGl2Painter { let u_sampler_loc = gl.get_uniform_location(&self.program, "u_sampler").unwrap(); gl.uniform1i(Some(&u_sampler_loc), 0); - for (clip_rect, mesh) in jobs { + for egui::ClippedMesh(clip_rect, mesh) in clipped_meshes { if let Some(gl_texture) = self.get_texture(mesh.texture_id) { gl.bind_texture(Gl::TEXTURE_2D, Some(gl_texture)); diff --git a/epaint/src/lib.rs b/epaint/src/lib.rs index 3a3b596d..18b32e08 100644 --- a/epaint/src/lib.rs +++ b/epaint/src/lib.rs @@ -114,8 +114,14 @@ pub struct ClippedShape( pub Shape, ); -/// A clip triangle and some textured triangles, all in points (logical pixels). -pub type PaintJob = (emath::Rect, Mesh); - -/// Grouped by clip rectangles, in points (logical pixels). -pub type PaintJobs = Vec; +/// A [`Mesh`] within a clip rectangle. +/// +/// Everything is using logical points. +#[derive(Clone, Debug)] +pub struct ClippedMesh( + /// Clip / scissor rectangle. + /// Only show the part of the [`Mesh`] that falls within this. + pub emath::Rect, + /// The shape + pub Mesh, +); diff --git a/epaint/src/stats.rs b/epaint/src/stats.rs index f7ec59c0..5fea25cd 100644 --- a/epaint/src/stats.rs +++ b/epaint/src/stats.rs @@ -107,17 +107,17 @@ impl AllocInfo { pub fn format(&self, what: &str) -> String { if self.num_allocs() == 0 { - format!("{:6} {:12}", 0, what) + format!("{:6} {:14}", 0, what) } else if self.num_allocs() == 1 { format!( - "{:6} {:12} {} 1 allocation", + "{:6} {:14} {} 1 allocation", self.num_elements, what, self.megabytes() ) } else if self.element_size != ElementSize::Heterogenous { format!( - "{:6} {:12} {} {:3} allocations", + "{:6} {:14} {} {:3} allocations", self.num_elements(), what, self.megabytes(), @@ -125,7 +125,7 @@ impl AllocInfo { ) } else { format!( - "{:6} {:12} {} {:3} allocations", + "{:6} {:14} {} {:3} allocations", "", what, self.megabytes(), @@ -145,7 +145,7 @@ pub struct PaintStats { pub shape_vec: AllocInfo, /// Number of separate clip rectangles - pub jobs: AllocInfo, + pub clipped_meshes: AllocInfo, pub vertices: AllocInfo, pub indices: AllocInfo, } @@ -188,9 +188,9 @@ impl PaintStats { } } - pub fn with_paint_jobs(mut self, paint_jobs: &[crate::PaintJob]) -> Self { - self.jobs += AllocInfo::from_slice(paint_jobs); - for (_, indices) in paint_jobs { + pub fn with_clipped_meshes(mut self, clipped_meshes: &[crate::ClippedMesh]) -> Self { + self.clipped_meshes += AllocInfo::from_slice(clipped_meshes); + for ClippedMesh(_, indices) in clipped_meshes { self.vertices += AllocInfo::from_slice(&indices.vertices); self.indices += AllocInfo::from_slice(&indices.indices); } @@ -202,7 +202,7 @@ impl PaintStats { // + self.shape_text // + self.shape_path // + self.shape_mesh - // + self.jobs + // + self.clipped_meshes // + self.vertices // + self.indices // } diff --git a/epaint/src/tessellator.rs b/epaint/src/tessellator.rs index a15f830e..ff2046e9 100644 --- a/epaint/src/tessellator.rs +++ b/epaint/src/tessellator.rs @@ -668,27 +668,28 @@ pub fn tessellate_shapes( shapes: Vec, options: TessellationOptions, fonts: &Fonts, -) -> Vec<(Rect, Mesh)> { +) -> Vec { let mut tessellator = Tessellator::from_options(options); - let mut jobs = PaintJobs::default(); + let mut clipped_meshes: Vec = Vec::default(); + for ClippedShape(clip_rect, shape) in shapes { - let start_new_job = match jobs.last() { + let start_new_mesh = match clipped_meshes.last() { None => true, - Some(job) => job.0 != clip_rect || job.1.texture_id != shape.texture_id(), + Some(cm) => cm.0 != clip_rect || cm.1.texture_id != shape.texture_id(), }; - if start_new_job { - jobs.push((clip_rect, Mesh::default())); + if start_new_mesh { + clipped_meshes.push(ClippedMesh(clip_rect, Mesh::default())); } - let out = &mut jobs.last_mut().unwrap().1; + let out = &mut clipped_meshes.last_mut().unwrap().1; tessellator.clip_rect = clip_rect; tessellator.tessellate_shape(fonts, shape, out); } if options.debug_paint_clip_rects { - for (clip_rect, mesh) in &mut jobs { + for ClippedMesh(clip_rect, mesh) in &mut clipped_meshes { tessellator.clip_rect = Rect::everything(); tessellator.tessellate_shape( fonts, @@ -704,14 +705,14 @@ pub fn tessellate_shapes( } if options.debug_ignore_clip_rects { - for (clip_rect, _) in &mut jobs { + for ClippedMesh(clip_rect, _) in &mut clipped_meshes { *clip_rect = Rect::everything(); } } - for (_, mesh) in &jobs { + for ClippedMesh(_, mesh) in &clipped_meshes { debug_assert!(mesh.is_valid(), "Tessellator generated invalid Mesh"); } - jobs + clipped_meshes }