Back out of wp_viewporter
This commit is contained in:
@@ -40,7 +40,6 @@ use wayland_client::{
|
|||||||
use wayland_protocols::wp::cursor_shape::v1::client::{
|
use wayland_protocols::wp::cursor_shape::v1::client::{
|
||||||
wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,
|
wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,
|
||||||
};
|
};
|
||||||
use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
|
|
||||||
use wayland_protocols::wp::primary_selection::zv1::client::{
|
use wayland_protocols::wp::primary_selection::zv1::client::{
|
||||||
zwp_primary_selection_device_manager_v1, zwp_primary_selection_device_v1,
|
zwp_primary_selection_device_manager_v1, zwp_primary_selection_device_v1,
|
||||||
zwp_primary_selection_offer_v1, zwp_primary_selection_source_v1,
|
zwp_primary_selection_offer_v1, zwp_primary_selection_source_v1,
|
||||||
@@ -157,9 +156,6 @@ struct State {
|
|||||||
clipboard_device: Option<wl_data_device::WlDataDevice>,
|
clipboard_device: Option<wl_data_device::WlDataDevice>,
|
||||||
cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,
|
cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,
|
||||||
cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
|
cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
|
||||||
/// Surface viewport for compositor-side scaling during resize. When set, the compositor
|
|
||||||
/// scales the last-committed buffer to the destination size without a GPU re-render.
|
|
||||||
viewport: Option<wp_viewport::WpViewport>,
|
|
||||||
primary_selection_manager:
|
primary_selection_manager:
|
||||||
Option<zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1>,
|
Option<zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1>,
|
||||||
primary_selection_device: Option<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1>,
|
primary_selection_device: Option<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1>,
|
||||||
@@ -316,7 +312,6 @@ impl WaylandWindow {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
let cursor_shape_manager = globals.bind(&qh, 1..=2, ()).ok();
|
let cursor_shape_manager = globals.bind(&qh, 1..=2, ()).ok();
|
||||||
let viewporter: Option<wp_viewporter::WpViewporter> = globals.bind(&qh, 1..=1, ()).ok();
|
|
||||||
let primary_selection_manager = globals.bind(&qh, 1..=1, ()).ok();
|
let primary_selection_manager = globals.bind(&qh, 1..=1, ()).ok();
|
||||||
let primary_selection_device = primary_selection_manager.as_ref().map(
|
let primary_selection_device = primary_selection_manager.as_ref().map(
|
||||||
|manager: &zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1| {
|
|manager: &zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1| {
|
||||||
@@ -324,7 +319,6 @@ impl WaylandWindow {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
let surface = compositor.create_surface(&qh, ());
|
let surface = compositor.create_surface(&qh, ());
|
||||||
let viewport = viewporter.as_ref().map(|vp| vp.get_viewport(&surface, &qh, ()));
|
|
||||||
let xdg_surface = wm_base.get_xdg_surface(&surface, &qh, ());
|
let xdg_surface = wm_base.get_xdg_surface(&surface, &qh, ());
|
||||||
let toplevel = xdg_surface.get_toplevel(&qh, ());
|
let toplevel = xdg_surface.get_toplevel(&qh, ());
|
||||||
toplevel.set_title(spec.title.clone());
|
toplevel.set_title(spec.title.clone());
|
||||||
@@ -369,7 +363,6 @@ impl WaylandWindow {
|
|||||||
clipboard_device,
|
clipboard_device,
|
||||||
cursor_shape_manager,
|
cursor_shape_manager,
|
||||||
cursor_shape_device: None,
|
cursor_shape_device: None,
|
||||||
viewport,
|
|
||||||
primary_selection_manager,
|
primary_selection_manager,
|
||||||
primary_selection_device,
|
primary_selection_device,
|
||||||
qh,
|
qh,
|
||||||
@@ -685,29 +678,6 @@ impl WaylandWindow {
|
|||||||
self.state.frame_callback = None;
|
self.state.frame_callback = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the compositor-side destination size for the surface viewport.
|
|
||||||
/// The compositor will scale the last-committed buffer to this size without a GPU re-render.
|
|
||||||
/// Call `clear_viewport_destination` before the next GPU render so the buffer size is used.
|
|
||||||
/// Returns `true` if the viewport was set (compositor supports wp_viewporter), `false` if
|
|
||||||
/// the caller must fall back to a GPU render for the preview.
|
|
||||||
fn set_viewport_destination(&mut self, width: u32, height: u32) -> bool {
|
|
||||||
if let Some(vp) = self.state.viewport.as_ref() {
|
|
||||||
vp.set_destination(width as i32, height as i32);
|
|
||||||
self.state._surface.commit();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove the compositor-side destination override. Must be called before the next GPU
|
|
||||||
/// render so the compositor uses the buffer's natural dimensions.
|
|
||||||
fn clear_viewport_destination(&mut self) {
|
|
||||||
if let Some(vp) = self.state.viewport.as_ref() {
|
|
||||||
vp.set_destination(-1, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_connection(&mut self) -> Result<(), Box<dyn Error>> {
|
fn flush_connection(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
self.state._connection.flush()?;
|
self.state._connection.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1137,6 +1107,7 @@ fn spawn_window_worker(
|
|||||||
internal_tx,
|
internal_tx,
|
||||||
keyboard_repeat_timer: None,
|
keyboard_repeat_timer: None,
|
||||||
pending_viewport_since: None,
|
pending_viewport_since: None,
|
||||||
|
pending_swapchain_size: None,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Task 1: Wayland-fd watcher. Waits for the compositor to buffer events (frame
|
// Task 1: Wayland-fd watcher. Waits for the compositor to buffer events (frame
|
||||||
@@ -1339,11 +1310,11 @@ fn spawn_window_worker(
|
|||||||
);
|
);
|
||||||
state_ref.pending_viewport = Some(current_viewport);
|
state_ref.pending_viewport = Some(current_viewport);
|
||||||
state_ref.pending_viewport_since.get_or_insert_with(Instant::now);
|
state_ref.pending_viewport_since.get_or_insert_with(Instant::now);
|
||||||
// Emit the Configured event BEFORE resizing the GPU
|
// Emit configure BEFORE touching the GPU so the app
|
||||||
// swapchain so the app thread starts layout immediately.
|
// thread starts layout immediately in parallel.
|
||||||
// renderer.resize() then runs concurrently with layout
|
// The actual swapchain recreation is deferred to just
|
||||||
// (different CPU threads), cutting the critical path from
|
// before the next renderer.render() call so rapid
|
||||||
// resize_gpu + layout to max(resize_gpu, layout).
|
// configures only pay one recreation.
|
||||||
if state_ref
|
if state_ref
|
||||||
.latest_scene
|
.latest_scene
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -1354,17 +1325,8 @@ fn spawn_window_worker(
|
|||||||
} else {
|
} else {
|
||||||
state_ref.pending_viewport = None;
|
state_ref.pending_viewport = None;
|
||||||
}
|
}
|
||||||
let t_resize = std::time::Instant::now();
|
state_ref.pending_swapchain_size =
|
||||||
state_ref.renderer.resize(frame.width, frame.height);
|
Some((frame.width, frame.height));
|
||||||
let resize_gpu_us = t_resize.elapsed().as_micros();
|
|
||||||
debug!(
|
|
||||||
target: "ruin_ui_platform_wayland::perf",
|
|
||||||
window_id = state_ref.window_id.raw(),
|
|
||||||
width = frame.width,
|
|
||||||
height = frame.height,
|
|
||||||
resize_gpu_us,
|
|
||||||
"renderer swapchain resized"
|
|
||||||
);
|
|
||||||
state_ref.window.request_redraw();
|
state_ref.window.request_redraw();
|
||||||
}
|
}
|
||||||
if !state_ref.opened_emitted {
|
if !state_ref.opened_emitted {
|
||||||
@@ -1376,11 +1338,10 @@ fn spawn_window_worker(
|
|||||||
let scene = state_ref.latest_scene.clone();
|
let scene = state_ref.latest_scene.clone();
|
||||||
if let Some(scene) = scene.as_ref() {
|
if let Some(scene) = scene.as_ref() {
|
||||||
if scene.logical_size != current_viewport {
|
if scene.logical_size != current_viewport {
|
||||||
// Resize case: render the preview immediately.
|
// Size mismatch: hold the last committed buffer and
|
||||||
// Drop any pending frame callback — vsync pacing
|
// wait for the app to produce a correctly-sized scene.
|
||||||
// doesn't matter for a placeholder preview, and
|
// Clear any pending frame callback so we can render
|
||||||
// waiting for it (potentially 16–150ms) is the
|
// the new scene as soon as it arrives.
|
||||||
// primary source of visible resize lag.
|
|
||||||
state_ref.window.clear_frame_callback();
|
state_ref.window.clear_frame_callback();
|
||||||
debug!(
|
debug!(
|
||||||
target: "ruin_ui_platform_wayland::resize",
|
target: "ruin_ui_platform_wayland::resize",
|
||||||
@@ -1390,76 +1351,33 @@ fn spawn_window_worker(
|
|||||||
scene_height = scene.logical_size.height,
|
scene_height = scene.logical_size.height,
|
||||||
viewport_width = current_viewport.width,
|
viewport_width = current_viewport.width,
|
||||||
viewport_height = current_viewport.height,
|
viewport_height = current_viewport.height,
|
||||||
"scene size does not match current viewport"
|
"scene size does not match current viewport; holding last buffer"
|
||||||
);
|
);
|
||||||
state_ref.pending_viewport = Some(current_viewport);
|
state_ref.pending_viewport = Some(current_viewport);
|
||||||
state_ref.pending_viewport_since.get_or_insert_with(Instant::now);
|
state_ref.pending_viewport_since.get_or_insert_with(Instant::now);
|
||||||
// Use wp_viewport to scale the last-committed buffer
|
state_ref.window.request_redraw();
|
||||||
// to the new size without any GPU work. This lets
|
|
||||||
// the compositor composite immediately after
|
|
||||||
// ack_configure, before a new GPU frame is ready.
|
|
||||||
// Falls back to a GPU stretch render when the
|
|
||||||
// compositor does not advertise wp_viewporter.
|
|
||||||
let viewport_set = state_ref.window
|
|
||||||
.set_viewport_destination(
|
|
||||||
current_viewport.width as u32,
|
|
||||||
current_viewport.height as u32,
|
|
||||||
);
|
|
||||||
let preview_ok = if viewport_set {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
let mut preview_scene = scene.clone();
|
|
||||||
preview_scene.logical_size = current_viewport;
|
|
||||||
state_ref.renderer.render(&preview_scene).is_ok()
|
|
||||||
};
|
|
||||||
if preview_ok {
|
|
||||||
match state_ref.window.flush_connection() {
|
|
||||||
Ok(()) => {
|
|
||||||
let preview_lag_us = state_ref
|
|
||||||
.pending_viewport_since
|
|
||||||
.map(|t| t.elapsed().as_micros());
|
|
||||||
debug!(
|
|
||||||
target: "ruin_ui_platform_wayland::resize",
|
|
||||||
window_id = state_ref.window_id.raw(),
|
|
||||||
scene_version = scene.version,
|
|
||||||
width = current_viewport.width,
|
|
||||||
height = current_viewport.height,
|
|
||||||
preview_lag_us,
|
|
||||||
compositor_scaled = viewport_set,
|
|
||||||
"presented preview; waiting for correct scene"
|
|
||||||
);
|
|
||||||
let _ = state_ref.event_tx.send(
|
|
||||||
PlatformEvent::FramePresented {
|
|
||||||
window_id: state_ref.window_id,
|
|
||||||
scene_version: scene.version,
|
|
||||||
item_count: scene.item_count(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
finish_presented_viewport_request(
|
|
||||||
&mut state_ref,
|
|
||||||
scene.logical_size,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
debug!(
|
|
||||||
target: "ruin_ui_platform_wayland::scene",
|
|
||||||
window_id = state_ref.window_id.raw(),
|
|
||||||
error = %error,
|
|
||||||
"failed to flush preview"
|
|
||||||
);
|
|
||||||
state_ref.window.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if !state_ref.window.presentation_ready() {
|
} else if !state_ref.window.presentation_ready() {
|
||||||
// Correct scene is ready but the compositor
|
// Correct scene is ready but the compositor
|
||||||
// hasn't signalled vsync yet. Wait for it.
|
// hasn't signalled vsync yet. Wait for it.
|
||||||
state_ref.window.request_redraw();
|
state_ref.window.request_redraw();
|
||||||
} else {
|
} else {
|
||||||
// Correct scene: clear the viewport destination so
|
// Correct scene: apply any deferred swapchain resize
|
||||||
// the compositor uses the buffer's natural size.
|
// before rendering. Deferring from frame.resized means
|
||||||
// This must happen before wgpu commits the new buffer.
|
// rapid configures pay one recreation instead of one
|
||||||
state_ref.window.clear_viewport_destination();
|
// per event.
|
||||||
|
if let Some((w, h)) = state_ref.pending_swapchain_size.take() {
|
||||||
|
let t_resize = std::time::Instant::now();
|
||||||
|
state_ref.renderer.resize(w, h);
|
||||||
|
let resize_gpu_us = t_resize.elapsed().as_micros();
|
||||||
|
debug!(
|
||||||
|
target: "ruin_ui_platform_wayland::perf",
|
||||||
|
window_id = state_ref.window_id.raw(),
|
||||||
|
width = w,
|
||||||
|
height = h,
|
||||||
|
resize_gpu_us,
|
||||||
|
"renderer swapchain resized (deferred)"
|
||||||
|
);
|
||||||
|
}
|
||||||
state_ref.window.arm_frame_callback();
|
state_ref.window.arm_frame_callback();
|
||||||
let t_render = std::time::Instant::now();
|
let t_render = std::time::Instant::now();
|
||||||
match state_ref.renderer.render(scene) {
|
match state_ref.renderer.render(scene) {
|
||||||
@@ -1655,8 +1573,6 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate_noop!(State: ignore wp_viewporter::WpViewporter);
|
|
||||||
delegate_noop!(State: ignore wp_viewport::WpViewport);
|
|
||||||
delegate_noop!(State: ignore wl_compositor::WlCompositor);
|
delegate_noop!(State: ignore wl_compositor::WlCompositor);
|
||||||
delegate_noop!(State: ignore wl_surface::WlSurface);
|
delegate_noop!(State: ignore wl_surface::WlSurface);
|
||||||
delegate_noop!(State: ignore wl_data_device_manager::WlDataDeviceManager);
|
delegate_noop!(State: ignore wl_data_device_manager::WlDataDeviceManager);
|
||||||
|
|||||||
Reference in New Issue
Block a user