Wrap tesselated output in struct ClippedMesh(Rect, Mesh)
This commit is contained in:
parent
75fa77e040
commit
b493bc6efc
|
|
@ -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 🐛
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,13 +583,14 @@ impl Context {
|
|||
}
|
||||
|
||||
/// Tessellate the given shapes into triangle meshes.
|
||||
pub fn tessellate(&self, shapes: Vec<ClippedShape>) -> PaintJobs {
|
||||
pub fn tessellate(&self, shapes: Vec<ClippedShape>) -> Vec<ClippedMesh> {
|
||||
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
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ pub fn run(mut app: Box<dyn epi::App>) -> ! {
|
|||
.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<dyn epi::App>) -> ! {
|
|||
&display,
|
||||
ctx.pixels_per_point(),
|
||||
app.clear_color(),
|
||||
paint_jobs,
|
||||
clipped_meshes,
|
||||
&ctx.texture(),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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::ClippedMesh>,
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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<egui::ClippedMesh>), 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<egui::ClippedMesh>,
|
||||
) -> 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<egui::ClippedMesh>), 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<egui::ClippedMesh>) -> Result<(), JsValue> {
|
||||
self.web_backend
|
||||
.paint(self.app.clear_color(), clipped_meshes)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<egui::ClippedMesh>,
|
||||
pixels_per_point: f32,
|
||||
) -> Result<(), JsValue>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<egui::ClippedMesh>,
|
||||
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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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<egui::ClippedMesh>,
|
||||
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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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<PaintJob>;
|
||||
/// 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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -668,27 +668,28 @@ pub fn tessellate_shapes(
|
|||
shapes: Vec<ClippedShape>,
|
||||
options: TessellationOptions,
|
||||
fonts: &Fonts,
|
||||
) -> Vec<(Rect, Mesh)> {
|
||||
) -> Vec<ClippedMesh> {
|
||||
let mut tessellator = Tessellator::from_options(options);
|
||||
|
||||
let mut jobs = PaintJobs::default();
|
||||
let mut clipped_meshes: Vec<ClippedMesh> = 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue