Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Add the following to your `Cargo.toml`:

```toml
[dependencies]
grafo = "0.10"
grafo = "0.18"
winit = "0.30" # For window creation and event handling
image = "0.25" # For image decoding (textures)
env_logger = "0.11" # For logging
Expand Down 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,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 @@ -287,20 +292,21 @@ impl<'a> ApplicationHandler for App<'a> {
.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