Split out tesselation from Context::end_frame()

This commit is contained in:
Emil Ernerfeldt 2020-11-07 11:44:32 +01:00
parent 901a6920be
commit af11d766fc
9 changed files with 59 additions and 29 deletions

View file

@ -13,6 +13,7 @@
* You can no longer throw windows * You can no longer throw windows
* `Context::begin_frame()` no longer returns anything. * `Context::begin_frame()` no longer returns anything.
* Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`. * Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
* `Context::end_frame()` now returns "paint jobs" that need to be converted to triangles with `Context::tesselate()`.
## 0.2.0 - 2020-10-10 ## 0.2.0 - 2020-10-10

View file

@ -154,7 +154,8 @@ loop {
let raw_input: egui::RawInput = my_integration.gather_input(); let raw_input: egui::RawInput = my_integration.gather_input();
egui_ctx.begin_frame(raw_input); egui_ctx.begin_frame(raw_input);
my_app.ui(&mut egui_ctx); // add panels, windows and widgets to `egui_ctx` here my_app.ui(&mut egui_ctx); // add panels, windows and widgets to `egui_ctx` here
let (output, paint_jobs) = egui_ctx.end_frame(); let (output, paint_commands) = egui_ctx.end_frame();
let paint_jobs = self.ctx.tesselate(paint_commands); // create triangles to paint
my_integration.paint(paint_jobs); my_integration.paint(paint_jobs);
my_integration.set_cursor_icon(output.cursor_icon); my_integration.set_cursor_icon(output.cursor_icon);
// Also see `egui::Output` for more // Also see `egui::Output` for more

View file

@ -33,6 +33,19 @@ pub fn criterion_benchmark(c: &mut Criterion) {
}); });
} }
{
let mut ctx = egui::Context::new();
ctx.memory().all_collpasing_are_open = true; // expand the demo window with everything
let mut demo_windows = egui::demos::DemoWindows::default();
ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx, &Default::default(), &mut None);
let (_, paint_commands) = ctx.end_frame();
c.bench_function("tesselate", |b| {
b.iter(|| ctx.tesselate(paint_commands.clone()))
});
}
{ {
let mut ctx = egui::Context::new(); let mut ctx = egui::Context::new();
ctx.begin_frame(raw_input); ctx.begin_frame(raw_input);
@ -43,7 +56,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
}) })
}); });
}); });
// let _ = ctx.end_frame(); // skip, because tessellating all that text is slow let _ = ctx.end_frame();
} }
} }

View file

@ -20,7 +20,7 @@ struct Options {
/// The default style for new `Ui`:s. /// The default style for new `Ui`:s.
style: Arc<Style>, style: Arc<Style>,
/// Controls the tessellator. /// Controls the tessellator.
paint_options: paint::PaintOptions, tesselation_options: paint::TesselationOptions,
/// Font sizes etc. /// Font sizes etc.
font_definitions: FontDefinitions, font_definitions: FontDefinitions,
} }
@ -242,8 +242,10 @@ impl Context {
/// Call at the end of each frame. /// Call at the end of each frame.
/// Returns what has happened this frame (`Output`) as well as what you need to paint. /// Returns what has happened this frame (`Output`) as well as what you need to paint.
/// You can transform the returned paint commands into triangles with a call to
/// `Context::tesselate`.
#[must_use] #[must_use]
pub fn end_frame(&self) -> (Output, PaintJobs) { pub fn end_frame(&self) -> (Output, Vec<(Rect, PaintCmd)>) {
if self.input.wants_repaint() { if self.input.wants_repaint() {
self.request_repaint(); self.request_repaint();
} }
@ -256,8 +258,8 @@ impl Context {
output.needs_repaint = true; output.needs_repaint = true;
} }
let paint_jobs = self.paint(); let paint_commands = self.drain_paint_lists();
(output, paint_jobs) (output, paint_commands)
} }
fn drain_paint_lists(&self) -> Vec<(Rect, PaintCmd)> { fn drain_paint_lists(&self) -> Vec<(Rect, PaintCmd)> {
@ -265,15 +267,17 @@ impl Context {
self.graphics().drain(memory.areas.order()).collect() self.graphics().drain(memory.areas.order()).collect()
} }
fn paint(&self) -> PaintJobs { /// Tesselate the given paint commands into triangle meshes.
let mut paint_options = self.options.lock().paint_options; pub fn tesselate(&self, paint_commands: Vec<(Rect, PaintCmd)>) -> PaintJobs {
paint_options.aa_size = 1.0 / self.pixels_per_point(); let mut tesselation_options = self.options.lock().tesselation_options;
let paint_commands = self.drain_paint_lists(); tesselation_options.aa_size = 1.0 / self.pixels_per_point();
let paint_stats = PaintStats::from_paint_commands(&paint_commands); // TODO: internal allocations let paint_stats = PaintStats::from_paint_commands(&paint_commands); // TODO: internal allocations
let paint_jobs = let paint_jobs = tessellator::tessellate_paint_commands(
tessellator::tessellate_paint_commands(paint_commands, paint_options, self.fonts()); paint_commands,
tesselation_options,
self.fonts(),
);
*self.paint_stats.lock() = paint_stats.with_paint_jobs(&paint_jobs); *self.paint_stats.lock() = paint_stats.with_paint_jobs(&paint_jobs);
paint_jobs paint_jobs
} }
@ -591,9 +595,9 @@ impl Context {
CollapsingHeader::new("Painting") CollapsingHeader::new("Painting")
.default_open(true) .default_open(true)
.show(ui, |ui| { .show(ui, |ui| {
let mut paint_options = self.options.lock().paint_options; let mut tesselation_options = self.options.lock().tesselation_options;
paint_options.ui(ui); tesselation_options.ui(ui);
self.options.lock().paint_options = paint_options; self.options.lock().tesselation_options = tesselation_options;
}); });
} }
@ -708,7 +712,7 @@ impl Context {
} }
} }
impl paint::PaintOptions { impl paint::TesselationOptions {
pub fn ui(&mut self, ui: &mut Ui) { pub fn ui(&mut self, ui: &mut Ui) {
let Self { let Self {
aa_size: _, aa_size: _,

View file

@ -26,7 +26,8 @@
//! let raw_input: egui::RawInput = my_integration.gather_input(); //! let raw_input: egui::RawInput = my_integration.gather_input();
//! egui_ctx.begin_frame(raw_input); //! egui_ctx.begin_frame(raw_input);
//! my_app.ui(&egui_ctx); // add panels, windows and widgets to `egui_ctx` here //! my_app.ui(&egui_ctx); // add panels, windows and widgets to `egui_ctx` here
//! let (output, paint_jobs) = egui_ctx.end_frame(); //! let (output, paint_commands) = egui_ctx.end_frame();
//! let paint_jobs = self.ctx.tesselate(paint_commands); // create triangles to paint
//! my_integration.paint(paint_jobs); //! my_integration.paint(paint_jobs);
//! my_integration.set_cursor_icon(output.cursor_icon); //! my_integration.set_cursor_icon(output.cursor_icon);
//! // Also see `egui::Output` for more //! // Also see `egui::Output` for more
@ -116,7 +117,8 @@ pub fn text_egui_e2e() {
for _ in 0..NUM_FRAMES { for _ in 0..NUM_FRAMES {
ctx.begin_frame(raw_input.clone()); ctx.begin_frame(raw_input.clone());
demo_windows.ui(&ctx, &Default::default(), &mut None); demo_windows.ui(&ctx, &Default::default(), &mut None);
let (_output, paint_jobs) = ctx.end_frame(); let (_output, paint_commands) = ctx.end_frame();
let paint_jobs = ctx.tesselate(paint_commands);
assert!(!paint_jobs.is_empty()); assert!(!paint_jobs.is_empty());
} }
} }

View file

@ -15,6 +15,8 @@ pub use {
command::{PaintCmd, Stroke}, command::{PaintCmd, Stroke},
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle}, fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
stats::PaintStats, stats::PaintStats,
tessellator::{PaintJob, PaintJobs, PaintOptions, TextureId, Triangles, Vertex, WHITE_UV}, tessellator::{
PaintJob, PaintJobs, TesselationOptions, TextureId, Triangles, Vertex, WHITE_UV,
},
texture_atlas::Texture, texture_atlas::Texture,
}; };

View file

@ -446,7 +446,7 @@ use self::PathType::{Closed, Open};
/// Tesselation quality options /// Tesselation quality options
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct PaintOptions { pub struct TesselationOptions {
/// Size of a pixel in points, e.g. 0.5 /// Size of a pixel in points, e.g. 0.5
pub aa_size: f32, pub aa_size: f32,
/// Anti-aliasing makes shapes appear smoother, but requires more triangles and is therefore slower. /// Anti-aliasing makes shapes appear smoother, but requires more triangles and is therefore slower.
@ -459,20 +459,25 @@ pub struct PaintOptions {
pub debug_ignore_clip_rects: bool, pub debug_ignore_clip_rects: bool,
} }
impl Default for PaintOptions { impl Default for TesselationOptions {
fn default() -> Self { fn default() -> Self {
Self { Self {
aa_size: 1.0, aa_size: 1.0,
anti_alias: true, anti_alias: true,
coarse_tessellation_culling: true,
debug_paint_clip_rects: false, debug_paint_clip_rects: false,
debug_ignore_clip_rects: false, debug_ignore_clip_rects: false,
coarse_tessellation_culling: true,
} }
} }
} }
/// Tesselate the given convex area into a polygon. /// Tesselate the given convex area into a polygon.
fn fill_closed_path(path: &[PathPoint], color: Srgba, options: PaintOptions, out: &mut Triangles) { fn fill_closed_path(
path: &[PathPoint],
color: Srgba,
options: TesselationOptions,
out: &mut Triangles,
) {
if color == color::TRANSPARENT { if color == color::TRANSPARENT {
return; return;
} }
@ -516,7 +521,7 @@ fn stroke_path(
path: &[PathPoint], path: &[PathPoint],
path_type: PathType, path_type: PathType,
stroke: Stroke, stroke: Stroke,
options: PaintOptions, options: TesselationOptions,
out: &mut Triangles, out: &mut Triangles,
) { ) {
if stroke.width <= 0.0 || stroke.color == color::TRANSPARENT { if stroke.width <= 0.0 || stroke.color == color::TRANSPARENT {
@ -669,7 +674,7 @@ fn mul_color(color: Srgba, factor: f32) -> Srgba {
fn tessellate_paint_command( fn tessellate_paint_command(
clip_rect: Rect, clip_rect: Rect,
command: PaintCmd, command: PaintCmd,
options: PaintOptions, options: TesselationOptions,
fonts: &Fonts, fonts: &Fonts,
out: &mut Triangles, out: &mut Triangles,
scratchpad_points: &mut Vec<Pos2>, scratchpad_points: &mut Vec<Pos2>,
@ -833,7 +838,7 @@ fn tessellate_paint_command(
/// A list of clip rectangles with matching `Triangles`. /// A list of clip rectangles with matching `Triangles`.
pub fn tessellate_paint_commands( pub fn tessellate_paint_commands(
commands: Vec<(Rect, PaintCmd)>, commands: Vec<(Rect, PaintCmd)>,
options: PaintOptions, options: TesselationOptions,
fonts: &Fonts, fonts: &Fonts,
) -> Vec<(Rect, Triangles)> { ) -> Vec<(Rect, Triangles)> {
let mut scratchpad_points = Vec::new(); let mut scratchpad_points = Vec::new();

View file

@ -89,7 +89,8 @@ pub fn run(
}; };
app.ui(&ctx, &mut integration_context); app.ui(&ctx, &mut integration_context);
let app_output = integration_context.output; let app_output = integration_context.output;
let (egui_output, paint_jobs) = ctx.end_frame(); let (egui_output, paint_commands) = ctx.end_frame();
let paint_jobs = ctx.tesselate(paint_commands);
let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32; let frame_time = (Instant::now() - egui_start).as_secs_f64() as f32;
previous_frame_time = Some(frame_time); previous_frame_time = Some(frame_time);

View file

@ -44,7 +44,8 @@ impl WebBackend {
.take() .take()
.expect("unmatched calls to begin_frame/end_frame"); .expect("unmatched calls to begin_frame/end_frame");
let (output, paint_jobs) = self.ctx.end_frame(); let (output, paint_commands) = self.ctx.end_frame();
let paint_jobs = self.ctx.tesselate(paint_commands);
self.auto_save(); self.auto_save();