-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP benchmarking and testing for renderers
This shows `iced_glow` outperforming `iced_wgpu`. Probably not accurate, something may be wrong in the rendering and timing here? It should also test with more primitivies. Tests pass when combined with #1485 and #1491.
- Loading branch information
Showing
8 changed files
with
612 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
[package] | ||
name = "iced_bench" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
iced = { path = "..", features = ["image"] } | ||
iced_glow = { path = "../glow" } | ||
iced_glutin = { path = "../glutin" } | ||
iced_graphics = { path = "../graphics" } | ||
iced_native = { path = "../native" } | ||
iced_wgpu = { path = "../wgpu" } | ||
|
||
[dependencies.image_rs] | ||
version = "0.23" | ||
package = "image" | ||
features = ["png"] | ||
default-features = false | ||
|
||
[dev-dependencies] | ||
criterion = "0.4.0" | ||
rand = "0.8.5" | ||
|
||
[[bench]] | ||
name = "renderer_bench" | ||
harness = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
use criterion::{black_box, criterion_group, criterion_main, Criterion}; | ||
|
||
use iced_native::{widget, Renderer}; | ||
|
||
use iced_bench::{glow::GlowBench, render_widget, wgpu::WgpuBench, Bench}; | ||
|
||
static LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; | ||
|
||
fn image(size: u32) -> iced_native::image::Handle { | ||
let mut bytes = Vec::with_capacity(size as usize * size as usize * 4); | ||
for _y in 0..size { | ||
for _x in 0..size { | ||
let r = rand::random(); | ||
let g = rand::random(); | ||
let b = rand::random(); | ||
bytes.extend_from_slice(&[r, g, b, 255]); | ||
} | ||
} | ||
iced_native::image::Handle::from_pixels(size, size, bytes) | ||
} | ||
|
||
fn text_primitive< | ||
R: iced_native::Renderer<Theme = iced::Theme> + iced_native::text::Renderer, | ||
>( | ||
renderer: &R, | ||
) -> iced_graphics::Primitive { | ||
iced_graphics::Primitive::Text { | ||
content: LOREM_IPSUM.to_string(), | ||
bounds: iced::Rectangle::with_size(iced::Size::new(1024.0, 1024.0)), | ||
color: iced_native::Color::BLACK, | ||
size: f32::from(renderer.default_size()), | ||
font: Default::default(), | ||
horizontal_alignment: iced_native::alignment::Horizontal::Left, | ||
vertical_alignment: iced_native::alignment::Vertical::Top, | ||
} | ||
} | ||
|
||
fn text_widget< | ||
R: iced_native::Renderer<Theme = iced::Theme> + iced_native::text::Renderer, | ||
>() -> widget::Text<'static, R> { | ||
widget::helpers::text(LOREM_IPSUM) | ||
} | ||
|
||
fn iter_render< | ||
B: Bench, | ||
F: FnMut(&mut iced_graphics::Renderer<B::Backend, iced::Theme>), | ||
>( | ||
b: &mut criterion::Bencher, | ||
bench: &mut B, | ||
mut draw_cb: F, | ||
) { | ||
b.iter(|| { | ||
bench.renderer().clear(); | ||
let state = bench.clear(); | ||
draw_cb(bench.renderer()); | ||
bench.present(state); | ||
}) | ||
} | ||
|
||
fn bench_function< | ||
B: Bench, | ||
F: FnMut(&mut iced_graphics::Renderer<B::Backend, iced::Theme>), | ||
>( | ||
c: &mut Criterion, | ||
bench: &mut B, | ||
id: &str, | ||
mut draw_cb: F, | ||
) { | ||
c.bench_function(&format!("{} {}", B::BACKEND_NAME, id), |b| { | ||
iter_render(b, bench, |backend| draw_cb(backend)); | ||
}); | ||
|
||
// Write output to file, so there's a way to see that generated | ||
// image is correct. | ||
let dir = std::path::Path::new(env!("CARGO_TARGET_TMPDIR")) | ||
.join(format!("bench-renderers/{}", B::BACKEND_NAME)); | ||
std::fs::create_dir_all(&dir).unwrap(); | ||
bench | ||
.read_pixels() | ||
.save(&dir.join(&format!("{}.png", id))) | ||
.unwrap(); | ||
} | ||
|
||
fn generic_benchmark<B: Bench>(c: &mut Criterion, bench: &mut B) | ||
where | ||
B::Backend: iced_graphics::backend::Text, | ||
{ | ||
bench_function(c, bench, "draw no primitive", |_renderer| {}); | ||
bench_function(c, bench, "draw quad primitive", |renderer| { | ||
renderer.draw_primitive(black_box(iced_graphics::Primitive::Quad { | ||
bounds: iced::Rectangle::with_size(iced::Size::new(256.0, 256.0)), | ||
background: iced_native::Background::Color( | ||
iced_native::Color::BLACK, | ||
), | ||
border_radius: 0., | ||
border_width: 0., | ||
border_color: Default::default(), | ||
})); | ||
}); | ||
bench_function(c, bench, "draw text primitive", |renderer| { | ||
renderer.draw_primitive(black_box(text_primitive(renderer))); | ||
}); | ||
let widget = text_widget(); | ||
bench_function(c, bench, "render text", |renderer| { | ||
render_widget(&widget, renderer); | ||
}); | ||
let handle = image(1024); | ||
let bounds = iced::Rectangle::with_size(iced::Size::new(1024.0, 1024.0)); | ||
bench_function(c, bench, "draw image primitive", |renderer| { | ||
renderer.draw_primitive(iced_graphics::Primitive::Image { | ||
handle: handle.clone(), | ||
bounds, | ||
}); | ||
}); | ||
} | ||
|
||
fn glow_benchmark(c: &mut Criterion) { | ||
let mut bench = GlowBench::new(1024, 1024); | ||
|
||
generic_benchmark(c, &mut bench); | ||
} | ||
|
||
fn wgpu_benchmark(c: &mut Criterion) { | ||
let mut bench = WgpuBench::new(1024, 1024); | ||
|
||
generic_benchmark(c, &mut bench); | ||
|
||
let widget = widget::helpers::image(image(1024)); | ||
bench_function(c, &mut bench, "render image", |renderer| { | ||
render_widget(&widget, renderer); | ||
}); | ||
} | ||
|
||
criterion_group!(benches, glow_benchmark, wgpu_benchmark); | ||
criterion_main!(benches); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
use iced_glow::glow::{self, HasContext}; | ||
use iced_glutin::glutin; | ||
|
||
fn glutin_context( | ||
width: u32, | ||
height: u32, | ||
) -> glutin::Context<glutin::NotCurrent> { | ||
let el = glutin::event_loop::EventLoop::new(); | ||
#[cfg(target_os = "linux")] | ||
use glutin::platform::unix::HeadlessContextExt; | ||
#[cfg(target_os = "linux")] | ||
if let Ok(context) = glutin::ContextBuilder::new() | ||
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0))) | ||
.build_surfaceless(&el) | ||
{ | ||
return context; | ||
} | ||
glutin::ContextBuilder::new() | ||
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (2, 0))) | ||
.build_headless(&el, glutin::dpi::PhysicalSize::new(width, height)) | ||
.unwrap() | ||
} | ||
|
||
pub struct GlowBench { | ||
_context: glutin::Context<glutin::PossiblyCurrent>, | ||
gl: glow::Context, | ||
renderer: iced_glow::Renderer<iced::Theme>, | ||
viewport: iced_graphics::Viewport, | ||
_framebuffer: glow::NativeFramebuffer, | ||
_renderbuffer: glow::NativeRenderbuffer, | ||
width: u32, | ||
height: u32, | ||
} | ||
|
||
impl GlowBench { | ||
pub fn new(width: u32, height: u32) -> Self { | ||
let context = | ||
unsafe { glutin_context(width, height).make_current().unwrap() }; | ||
let (gl, framebuffer, renderbuffer); | ||
unsafe { | ||
gl = glow::Context::from_loader_function(|name| { | ||
context.get_proc_address(name) | ||
}); | ||
gl.viewport(0, 0, width as i32, height as i32); | ||
|
||
renderbuffer = gl.create_renderbuffer().unwrap(); | ||
gl.bind_renderbuffer(glow::RENDERBUFFER, Some(renderbuffer)); | ||
gl.renderbuffer_storage( | ||
glow::RENDERBUFFER, | ||
glow::RGBA8, | ||
width as i32, | ||
height as i32, | ||
); | ||
gl.bind_renderbuffer(glow::RENDERBUFFER, None); | ||
|
||
framebuffer = gl.create_framebuffer().unwrap(); | ||
gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer)); | ||
gl.framebuffer_renderbuffer( | ||
glow::FRAMEBUFFER, | ||
glow::COLOR_ATTACHMENT0, | ||
glow::RENDERBUFFER, | ||
Some(renderbuffer), | ||
); | ||
assert_eq!( | ||
gl.check_framebuffer_status(glow::FRAMEBUFFER), | ||
glow::FRAMEBUFFER_COMPLETE | ||
); | ||
}; | ||
let renderer = iced_glow::Renderer::<iced::Theme>::new( | ||
iced_glow::Backend::new(&gl, Default::default()), | ||
); | ||
let viewport = iced_graphics::Viewport::with_physical_size( | ||
iced::Size::new(width, height), | ||
1.0, | ||
); | ||
Self { | ||
_context: context, | ||
gl, | ||
renderer, | ||
viewport, | ||
_framebuffer: framebuffer, | ||
_renderbuffer: renderbuffer, | ||
width, | ||
height, | ||
} | ||
} | ||
} | ||
|
||
impl super::Bench for GlowBench { | ||
type Backend = iced_glow::Backend; | ||
type RenderState = (); | ||
const BACKEND_NAME: &'static str = "glow"; | ||
|
||
fn clear(&self) { | ||
unsafe { | ||
self.gl.clear_color(1., 1., 1., 1.); | ||
self.gl.clear(glow::COLOR_BUFFER_BIT); | ||
} | ||
} | ||
|
||
fn present(&mut self, _state: ()) { | ||
self.renderer.with_primitives(|backend, primitive| { | ||
backend.present::<&str>(&self.gl, primitive, &self.viewport, &[]); | ||
}); | ||
unsafe { self.gl.finish() }; | ||
} | ||
|
||
fn read_pixels(&self) -> image_rs::RgbaImage { | ||
let mut pixels = image_rs::RgbaImage::new(self.width, self.height); | ||
unsafe { | ||
self.gl.read_pixels( | ||
0, | ||
0, | ||
self.width as i32, | ||
self.height as i32, | ||
glow::RGBA, | ||
glow::UNSIGNED_BYTE, | ||
glow::PixelPackData::Slice(&mut pixels), | ||
); | ||
} | ||
image_rs::imageops::flip_vertical_in_place(&mut pixels); | ||
pixels | ||
} | ||
|
||
fn size(&self) -> (u32, u32) { | ||
(self.width, self.height) | ||
} | ||
|
||
fn renderer(&mut self) -> &mut iced_glow::Renderer<iced::Theme> { | ||
&mut self.renderer | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
pub mod glow; | ||
pub mod wgpu; | ||
|
||
pub enum Msg {} | ||
|
||
pub fn render_widget< | ||
R: iced_native::Renderer<Theme = iced::Theme>, | ||
W: iced_native::Widget<Msg, R>, | ||
>( | ||
widget: &W, | ||
renderer: &mut R, | ||
) { | ||
let size = iced::Size::new(1024.0, 1024.0); | ||
let node = iced_native::layout::Node::new(size); | ||
let layout = iced_native::Layout::new(&node); | ||
widget.draw( | ||
&iced_native::widget::Tree::empty(), | ||
renderer, | ||
&iced::Theme::Light, | ||
&Default::default(), | ||
layout, | ||
iced::Point::new(0.0, 0.0), | ||
&iced::Rectangle::with_size(size), | ||
); | ||
} | ||
|
||
pub trait Bench { | ||
type Backend: iced_graphics::Backend; | ||
type RenderState; | ||
const BACKEND_NAME: &'static str; | ||
|
||
fn clear(&self) -> Self::RenderState; | ||
fn present(&mut self, state: Self::RenderState); | ||
fn read_pixels(&self) -> image_rs::RgbaImage; | ||
fn size(&self) -> (u32, u32); | ||
fn renderer( | ||
&mut self, | ||
) -> &mut iced_graphics::Renderer<Self::Backend, iced::Theme>; | ||
} |
Oops, something went wrong.