Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ renderer
)
.unwrap();

// Render one frame (typical winit loop would call this on RedrawRequested)
renderer.render().unwrap();
renderer.clear_draw_queue();
// Render one frame (a typical winit loop would call this on RedrawRequested).
match renderer.prepare() {
grafo::PreparationOutcome::Ready => {
renderer.commit(None).unwrap();
renderer.clear_draw_queue();
}
// Keep the draw queue for a redraw requested by resize or unocclusion.
grafo::PreparationOutcome::Suspended => {}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

### Multiple independent windows
Expand Down
26 changes: 16 additions & 10 deletions examples/backdrop_blur.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/// *group* effect.
use futures::executor::block_on;
use grafo::{BackdropEffectConfig, BorderRadii, Shape};
use grafo::{Color, ShapeDrawCommandOptions, Stroke};
use grafo::{Color, PreparationOutcome, ShapeDrawCommandOptions, Stroke};
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
Expand Down Expand Up @@ -145,13 +145,18 @@
renderer.resize(new_size);
window.request_redraw();
}
WindowEvent::Occluded(false) => {
if let Some(window) = &self.window {
window.request_redraw();
}
}
WindowEvent::RedrawRequested => {
let (pw, ph) = renderer.size();

// ── Background content (shapes drawn behind the panel) ───
let bg = Shape::rect(
[(20.0, 20.0), (780.0, 580.0)],
Stroke::new(2.0, Color::BLACK),

Check warning on line 159 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
let bg_id = renderer
.add_shape(
Expand All @@ -165,7 +170,7 @@
// Colorful rectangles that will be visible through the panel
let r1 = Shape::rect(
[(60.0, 80.0), (260.0, 260.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),

Check warning on line 173 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
renderer
.add_shape(
Expand All @@ -178,7 +183,7 @@

let r2 = Shape::rect(
[(180.0, 160.0), (420.0, 380.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),

Check warning on line 186 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
renderer
.add_shape(
Expand All @@ -191,7 +196,7 @@

let r3 = Shape::rect(
[(340.0, 100.0), (560.0, 300.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),

Check warning on line 199 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
renderer
.add_shape(
Expand All @@ -204,7 +209,7 @@

let r4 = Shape::rect(
[(100.0, 350.0), (700.0, 540.0)],
Stroke::new(2.0, Color::rgb(0, 0, 0)),

Check warning on line 212 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
renderer
.add_shape(
Expand All @@ -220,7 +225,7 @@
// everything already drawn behind it.
let panel = Shape::rect(
[(120.0, 120.0), (520.0, 460.0)],
Stroke::new(2.0, Color::rgb(100, 100, 100)),

Check warning on line 228 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
let panel_id = renderer
.add_shape(
Expand All @@ -235,7 +240,7 @@
let panel_content = Shape::rounded_rect(
[(240.0, 240.0), (600.0, 540.0)],
BorderRadii::new(100.0),
Stroke::new(1.0, Color::rgb(80, 80, 80)),

Check warning on line 243 in examples/backdrop_blur.rs

View workflow job for this annotation

GitHub Actions / Test Suite

falling back to `f32` as the trait bound `f32: From<f64>` is not satisfied
);
renderer
.add_shape(
Expand Down Expand Up @@ -287,20 +292,21 @@
.expect("Failed to set backdrop effect");

// ── Render ───────────────────────────────────────────────
match renderer.render() {
let PreparationOutcome::Ready = renderer.prepare() else {
renderer.clear_draw_queue();
return;
};
let commit_result = renderer.commit(None);
match commit_result {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
renderer.resize(renderer.size())
}
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => renderer.resize(renderer.size()),

Err(wgpu::SurfaceError::Timeout) => {
// The window is not visible yet (still appearing, minimized, or fully
// covered). Ask for another redraw instead of dropping the frame for
// good — winit does not request one when the window becomes visible.
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
window.request_redraw();
}
Err(e) => eprintln!("{e:?}"),
}
Expand Down
27 changes: 16 additions & 11 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use futures::executor::block_on;
use grafo::Shape;
use grafo::{Color, ShapeDrawCommandOptions, Stroke};
use grafo::{Color, PreparationOutcome, ShapeDrawCommandOptions, Stroke};
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
Expand Down Expand Up @@ -75,6 +75,11 @@ impl<'a> ApplicationHandler for App<'a> {
renderer.resize(new_size);
window.request_redraw();
}
WindowEvent::Occluded(false) => {
if let Some(window) = &self.window {
window.request_redraw();
}
}
WindowEvent::RedrawRequested => {
// Define a simple rectangle shape
let rect = Shape::rect(
Expand Down Expand Up @@ -103,20 +108,20 @@ impl<'a> ApplicationHandler for App<'a> {
)
.unwrap();

match renderer.render() {
let PreparationOutcome::Ready = renderer.prepare() else {
renderer.clear_draw_queue();
return;
};
let commit_result = renderer.commit(None);
match commit_result {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
renderer.resize(renderer.size())
}
Err(wgpu::SurfaceError::Timeout) => {
// The window is not visible yet (still appearing, minimized, or
// fully covered). Ask for another redraw instead of dropping the
// frame for good — winit does not request one when the window
// becomes visible again.
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => renderer.resize(renderer.size()),
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
window.request_redraw();
}
Err(e) => eprintln!("{e:?}"),
}
Expand Down
73 changes: 49 additions & 24 deletions examples/bench_render_loop.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
/// Renderer performance benchmark — two scenarios, both using the real `render()` path
/// Renderer performance benchmark — two scenarios, both using the real `prepare()`/`commit()` path
/// (present to screen, vsync OFF).
///
/// **Benchmark 1 — Static scene:**
/// Build the scene once, then render() repeatedly. Measures pure GPU + present cost.
/// Build the scene once, then prepare and commit repeatedly. Measures pure GPU + present cost.
///
/// **Benchmark 2 — Dynamic scene (re-add every frame):**
/// Each frame: clear_draw_queue() → rebuild all cached shapes → render().
/// Each frame: clear_draw_queue() → rebuild all cached shapes → prepare and commit.
/// Simulates a real UI where the render queue is reconstructed each frame.
///
/// Build and run with:
/// ```
/// cargo run --example bench_render_loop --features render_metrics --release
/// ```
use futures::executor::block_on;
use grafo::{Color, Shape, ShapeDrawCommandOptions, Stroke, TransformInstance};
use grafo::{Color, PreparationOutcome, Shape, ShapeDrawCommandOptions, Stroke, TransformInstance};
use std::sync::Arc;
use std::time::{Duration, Instant};
use winit::application::ApplicationHandler;
Expand Down Expand Up @@ -479,20 +479,30 @@ impl<'a> ApplicationHandler for BenchApp<'a> {
) {
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Occluded(false) => {
if let Some(window) = &self.window {
window.request_redraw();
}
}
WindowEvent::RedrawRequested => {
let window = self.window.clone().unwrap();

match self.phase {
Phase::WarmupStatic => {
let renderer = self.renderer.as_mut().unwrap();
match renderer.render() {
let PreparationOutcome::Ready = renderer.prepare() else {
return;
};
let commit_result = renderer.commit(None);
match commit_result {
Ok(_) => {}
Err(wgpu::SurfaceError::Timeout) => {
// Window not visible — skip without counting the frame.
window.request_redraw();
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
return;
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => {
let size = renderer.size();
renderer.resize(size);
window.request_redraw();
Expand All @@ -515,14 +525,19 @@ impl<'a> ApplicationHandler for BenchApp<'a> {
{
let renderer = self.renderer.as_mut().unwrap();
let frame_start = Instant::now();
match renderer.render() {
let PreparationOutcome::Ready = renderer.prepare() else {
return;
};
let commit_result = renderer.commit(None);
match commit_result {
Ok(_) => {}
Err(wgpu::SurfaceError::Timeout) => {
// Window not visible — skip without measuring the frame.
window.request_redraw();
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
return;
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => {
let size = renderer.size();
renderer.resize(size);
window.request_redraw();
Expand Down Expand Up @@ -556,15 +571,20 @@ impl<'a> ApplicationHandler for BenchApp<'a> {
Phase::WarmupDynamic => {
let renderer = self.renderer.as_mut().unwrap();
build_scene(renderer);
match renderer.render() {
let PreparationOutcome::Ready = renderer.prepare() else {
renderer.clear_draw_queue();
return;
};
let commit_result = renderer.commit(None);
match commit_result {
Ok(_) => {}
Err(wgpu::SurfaceError::Timeout) => {
// Window not visible — skip without counting the frame.
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
window.request_redraw();
return;
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => {
renderer.clear_draw_queue();
let size = renderer.size();
renderer.resize(size);
Expand Down Expand Up @@ -593,15 +613,20 @@ impl<'a> ApplicationHandler for BenchApp<'a> {
let rebuild_duration = rebuild_start.elapsed();

let frame_start = Instant::now();
match renderer.render() {
let PreparationOutcome::Ready = renderer.prepare() else {
renderer.clear_draw_queue();
return;
};
let commit_result = renderer.commit(None);
match commit_result {
Ok(_) => {}
Err(wgpu::SurfaceError::Timeout) => {
// Window not visible — skip without measuring the frame.
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
window.request_redraw();
return;
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => {
renderer.clear_draw_queue();
let size = renderer.size();
renderer.resize(size);
Expand Down
33 changes: 19 additions & 14 deletions examples/box_shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ impl<'a> ApplicationHandler for App<'a> {
renderer.resize(new_size);
window.request_redraw();
}
WindowEvent::Occluded(false) => {
if let Some(window) = &self.window {
window.request_redraw();
}
}
WindowEvent::RedrawRequested => {
let (pw, ph) = renderer.size();
let pw = pw as f32;
Expand Down Expand Up @@ -301,22 +306,22 @@ impl<'a> ApplicationHandler for App<'a> {
);

// ── Render ───────────────────────────────────────────────
match renderer.render() {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
renderer.resize(renderer.size())
}
if renderer.prepare() == grafo::PreparationOutcome::Ready {
match renderer.commit(None) {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => renderer.resize(renderer.size()),

Err(wgpu::SurfaceError::Timeout) => {
// The window is not visible yet (still appearing, minimized, or fully
// covered). Ask for another redraw instead of dropping the frame for
// good — winit does not request one when the window becomes visible.
renderer.clear_draw_queue();
window.request_redraw();
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
}
Err(e) => eprintln!("{e:?}"),
}
Err(e) => eprintln!("{e:?}"),
} else {
renderer.clear_draw_queue();
}
}
_ => {}
Expand Down
33 changes: 19 additions & 14 deletions examples/gaussian_blur.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ impl<'a> ApplicationHandler for App<'a> {
renderer.resize(new_size);
window.request_redraw();
}
WindowEvent::Occluded(false) => {
if let Some(window) = &self.window {
window.request_redraw();
}
}
WindowEvent::RedrawRequested => {
let (pw, ph) = renderer.size();

Expand Down Expand Up @@ -269,22 +274,22 @@ impl<'a> ApplicationHandler for App<'a> {
.unwrap();

// ── Render ───────────────────────────────────────────────
match renderer.render() {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
renderer.resize(renderer.size())
}
if renderer.prepare() == grafo::PreparationOutcome::Ready {
match renderer.commit(None) {
Ok(_) => {
renderer.clear_draw_queue();
}
Err(grafo::RenderError::Surface(
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
)) => renderer.resize(renderer.size()),

Err(wgpu::SurfaceError::Timeout) => {
// The window is not visible yet (still appearing, minimized, or fully
// covered). Ask for another redraw instead of dropping the frame for
// good — winit does not request one when the window becomes visible.
renderer.clear_draw_queue();
window.request_redraw();
Err(grafo::RenderError::Surface(wgpu::SurfaceError::Timeout)) => {
renderer.clear_draw_queue();
}
Err(e) => eprintln!("{e:?}"),
}
Err(e) => eprintln!("{e:?}"),
} else {
renderer.clear_draw_queue();
}
}
_ => {}
Expand Down
Loading
Loading