use vulkan first and fallback to gl if driver errors
This commit is contained in:
+51
-13
@@ -49,6 +49,10 @@ pub struct AppState {
|
||||
width: u32,
|
||||
height: u32,
|
||||
pub exit: bool,
|
||||
|
||||
// Frame callback: set true when compositor says it's ready for next frame
|
||||
draw_ready: bool,
|
||||
last_frame: Instant,
|
||||
}
|
||||
|
||||
// Safety: we only use the raw pointers in single-threaded context
|
||||
@@ -98,6 +102,8 @@ pub fn run(
|
||||
width: 0,
|
||||
height: 0,
|
||||
exit: false,
|
||||
draw_ready: false,
|
||||
last_frame: Instant::now(),
|
||||
};
|
||||
|
||||
// One roundtrip to discover outputs.
|
||||
@@ -147,36 +153,66 @@ pub fn run(
|
||||
.context("failed to create renderer")?
|
||||
});
|
||||
|
||||
// Render loop.
|
||||
// Render loop driven by wl_surface.frame callbacks.
|
||||
// The compositor stops sending callbacks when the surface is fully occluded,
|
||||
// so the loop naturally idles to zero CPU when another app is fullscreen.
|
||||
let frame_duration = if fps > 0 {
|
||||
Some(Duration::from_nanos(1_000_000_000 / fps as u64))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Register the first frame callback then render immediately. On Wayland
|
||||
// the compositor only fires frame callbacks for commits that contain a
|
||||
// buffer; an empty commit would stall the loop forever on many compositors.
|
||||
// Rendering here attaches the first buffer and commits it together with the
|
||||
// frame callback so the compositor has something to display right away.
|
||||
{
|
||||
let wl_surface = state.layer.as_ref().unwrap().wl_surface();
|
||||
wl_surface.frame(&qh, wl_surface.clone());
|
||||
}
|
||||
if let Some(r) = state.renderer.as_mut() {
|
||||
r.render();
|
||||
}
|
||||
conn.flush().ok();
|
||||
|
||||
loop {
|
||||
// Non-blocking Wayland event dispatch.
|
||||
event_queue.dispatch_pending(&mut state).context("event dispatch")?;
|
||||
// Flush outgoing messages.
|
||||
// Block the thread until the compositor sends us any event (including
|
||||
// the frame callback). Zero CPU while occluded.
|
||||
event_queue.blocking_dispatch(&mut state).context("event dispatch")?;
|
||||
conn.flush().ok();
|
||||
|
||||
if state.exit {
|
||||
break;
|
||||
}
|
||||
|
||||
let frame_start = Instant::now();
|
||||
if !state.draw_ready {
|
||||
continue;
|
||||
}
|
||||
state.draw_ready = false;
|
||||
|
||||
// Optional fps cap: sleep the remainder of the frame budget.
|
||||
if let Some(dur) = frame_duration {
|
||||
let elapsed = state.last_frame.elapsed();
|
||||
if elapsed < dur {
|
||||
std::thread::sleep(dur - elapsed);
|
||||
}
|
||||
}
|
||||
state.last_frame = Instant::now();
|
||||
|
||||
// Register the NEXT frame callback before present() so that the
|
||||
// request is included in the same wl_surface.commit that wgpu
|
||||
// issues inside present().
|
||||
{
|
||||
let wl_surface = state.layer.as_ref().unwrap().wl_surface();
|
||||
wl_surface.frame(&qh, wl_surface.clone());
|
||||
}
|
||||
|
||||
if let Some(r) = state.renderer.as_mut() {
|
||||
r.render();
|
||||
}
|
||||
|
||||
// Cap frame rate if requested.
|
||||
if let Some(dur) = frame_duration {
|
||||
let elapsed = frame_start.elapsed();
|
||||
if elapsed < dur {
|
||||
std::thread::sleep(dur - elapsed);
|
||||
}
|
||||
}
|
||||
conn.flush().ok();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -220,7 +256,9 @@ impl CompositorHandler for AppState {
|
||||
fn frame(
|
||||
&mut self, _: &Connection, _: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface, _: u32,
|
||||
) {}
|
||||
) {
|
||||
self.draw_ready = true;
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self, _: &Connection, _: &QueueHandle<Self>,
|
||||
|
||||
Reference in New Issue
Block a user