use futures::executor::block_on;
use grafo::Shape;
use grafo::{Color, Stroke};
use std::num::NonZeroU32;
use std::sync::{Arc, RwLock};
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::window::{Window, WindowId};
#[derive(Default)]
struct App<'a> {
window: Option<Arc<Window>>,
renderer: Option<Arc<RwLock<grafo::Renderer<'a>>>>,
softbuffer_context: Option<softbuffer::Context<Arc<Window>>>,
softbuffer_surface: Option<softbuffer::Surface<Arc<Window>, Arc<Window>>>,
pending_resize: Option<(u32, u32)>,
frame_count: u64,
argb_buffer: Vec<u32>,
}
impl<'a> ApplicationHandler for App<'a> {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window = Arc::new(
event_loop
.create_window(
Window::default_attributes().with_title("Grafo + Softbuffer Hybrid Test"),
)
.unwrap(),
);
let window_size = window.inner_size();
let scale_factor = window.scale_factor();
let physical_size = (window_size.width, window_size.height);
let renderer = block_on(grafo::Renderer::new(
window.clone(),
physical_size,
scale_factor,
false, false, 1, ));
println!("\n=== Grafo + Softbuffer Hybrid Resize Test ===");
println!("This renders with GPU to an offscreen texture,");
println!("then copies to CPU and presents via softbuffer.");
println!("If resize jitter disappears, the issue is GPU presentation timing.\n");
let softbuffer_context = softbuffer::Context::new(window.clone()).unwrap();
let mut softbuffer_surface =
softbuffer::Surface::new(&softbuffer_context, window.clone()).unwrap();
softbuffer_surface
.resize(
NonZeroU32::new(physical_size.0).unwrap(),
NonZeroU32::new(physical_size.1).unwrap(),
)
.unwrap();
let renderer = Arc::new(RwLock::new(renderer));
self.window = Some(window);
self.renderer = Some(renderer);
self.softbuffer_context = Some(softbuffer_context);
self.softbuffer_surface = Some(softbuffer_surface);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let Some(window) = &self.window else { return };
let Some(renderer) = &mut self.renderer else {
return;
};
let Some(softbuffer_surface) = &mut self.softbuffer_surface else {
return;
};
if window_id != window.id() {
return;
}
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(physical_size) => {
println!(
"Resize event to ({}, {})",
physical_size.width, physical_size.height
);
self.pending_resize = Some((physical_size.width, physical_size.height));
if physical_size.width > 0 && physical_size.height > 0 {
softbuffer_surface
.resize(
NonZeroU32::new(physical_size.width).unwrap(),
NonZeroU32::new(physical_size.height).unwrap(),
)
.unwrap();
}
window.request_redraw();
}
WindowEvent::RedrawRequested => {
if let Some(pending) = self.pending_resize.take() {
println!("Applying resize to GPU renderer: {:?}", pending);
renderer.write().unwrap().resize(pending);
}
self.frame_count += 1;
let mut renderer_guard = renderer.write().unwrap();
renderer_guard.clear_draw_queue();
let window_size = window.inner_size();
let background = Shape::rect(
[
(0.0, 0.0),
(window_size.width as f32, window_size.height as f32),
],
Stroke::new(1.0, Color::rgb(0, 0, 0)),
);
let bg_id = renderer_guard.add_shape(background, None, None);
renderer_guard.set_shape_color(bg_id, Some(Color::rgb(255, 255, 200)));
let red_id = renderer_guard.add_shape(
Shape::rect(
[(0.0, 0.0), (200.0, 200.0)],
Stroke::new(1.0, Color::rgb(0, 0, 0)),
),
None,
None,
);
renderer_guard.set_shape_color(red_id, Some(Color::rgb(255, 0, 0)));
renderer_guard.set_shape_transform(red_id, grafo::TransformInstance::identity());
let blue_id = renderer_guard.add_shape(
Shape::rect(
[(0.0, 0.0), (200.0, 200.0)],
Stroke::new(1.0, Color::rgb(0, 0, 0)),
),
None,
None,
);
renderer_guard.set_shape_color(blue_id, Some(Color::rgb(0, 0, 255)));
renderer_guard.set_shape_transform(
blue_id,
grafo::TransformInstance::translation(220.0, 0.0),
);
let render_start = std::time::Instant::now();
let needed_len = (window_size.width as usize) * (window_size.height as usize);
if self.argb_buffer.len() < needed_len {
self.argb_buffer.resize(needed_len, 0);
}
renderer_guard.render_to_argb32(&mut self.argb_buffer);
let render_time = render_start.elapsed();
let copy_start = std::time::Instant::now();
let mut buffer = softbuffer_surface.buffer_mut().unwrap();
let count = buffer.len().min(self.argb_buffer.len());
buffer[..count].copy_from_slice(&self.argb_buffer[..count]);
buffer.present().unwrap();
let copy_time = copy_start.elapsed();
if self.frame_count.is_multiple_of(60) {
println!(
"Frame {}: GPU render: {:?}, CPU copy+present: {:?}, Total: {:?}",
self.frame_count,
render_time,
copy_time,
render_start.elapsed()
);
}
}
_ => {}
}
}
}
fn main() {
env_logger::init();
let event_loop = EventLoop::new().expect("to start an event loop");
let mut app = App::default();
event_loop.run_app(&mut app).unwrap();
}