mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
* claim: #9118 * Agent chat window: an off-screen chat window no longer stalls the main window (#9118) The chat popped out into its own window is an immediate viewport: eframe draws and presents it inside the main window's frame. Both surfaces waited for vsync, and a Wayland compositor sends no frame callbacks to a window it isn't showing, so the main window stalled behind the chat window's present. A surface other than the main window's is now configured with Mailbox, else Immediate, when the configured mode waits for vsync and the surface offers one (viewport_present_mode in the vendored egui-wgpu). The main window keeps vsync and still paces both.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
//! The window surface is clamped to the device's largest texture (#869): a window moved between
|
||||
//! monitors of different scale asked for 11512 x 6808 against a maximum of 8192 and the
|
||||
//! surface configure was a validation panic. The clamp lives in the vendored egui-wgpu.
|
||||
//! surface configure was a validation panic. The clamp lives in the vendored egui-wgpu, and so
|
||||
//! does the present mode of a window other than the main one (the agent chat popped out).
|
||||
|
||||
use egui_wgpu::clamp_surface_extent;
|
||||
use egui_wgpu::wgpu::PresentMode;
|
||||
use egui_wgpu::{clamp_surface_extent, viewport_present_mode};
|
||||
|
||||
#[test]
|
||||
fn a_window_that_fits_is_unchanged() {
|
||||
@@ -39,3 +41,24 @@ fn extreme_sizes_never_give_zero_or_exceed_the_limit() {
|
||||
assert!(s > 0.0 && s <= 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_popped_out_window_presents_without_waiting_for_the_display() {
|
||||
// Mesa's Vulkan on Wayland: the chat's window took the main window down to a crawl while
|
||||
// both waited for vsync in one frame.
|
||||
let wayland = [PresentMode::Fifo, PresentMode::Mailbox, PresentMode::Immediate];
|
||||
assert_eq!(viewport_present_mode(false, PresentMode::AutoVsync, &wayland), PresentMode::Mailbox);
|
||||
assert_eq!(viewport_present_mode(false, PresentMode::Fifo, &[PresentMode::Fifo, PresentMode::Immediate]), PresentMode::Immediate);
|
||||
// The main window keeps its vsync: it paces both.
|
||||
assert_eq!(viewport_present_mode(true, PresentMode::AutoVsync, &wayland), PresentMode::AutoVsync);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_surface_that_only_waits_and_a_chosen_mode_without_vsync_are_kept() {
|
||||
assert_eq!(viewport_present_mode(false, PresentMode::AutoVsync, &[PresentMode::Fifo]), PresentMode::AutoVsync);
|
||||
assert_eq!(viewport_present_mode(false, PresentMode::Fifo, &[]), PresentMode::Fifo);
|
||||
for configured in [PresentMode::AutoNoVsync, PresentMode::Immediate, PresentMode::Mailbox] {
|
||||
assert_eq!(viewport_present_mode(false, configured, &[PresentMode::Fifo, PresentMode::Mailbox]), configured);
|
||||
assert_eq!(viewport_present_mode(true, configured, &[PresentMode::Fifo]), configured);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+5
@@ -313,6 +313,11 @@ SolidCraft changes:
|
||||
and the frame is skipped with a repaint asked for when it is due. The streak and the deadline
|
||||
survive a recreate; a configure the device takes ends the streak, a recreate it asked for
|
||||
included. A recreate for a lost surface goes ahead as before.
|
||||
- `src/lib.rs`, `src/winit.rs`: a window other than the main one (an immediate viewport, drawn
|
||||
and presented inside the main window's frame) is configured with Mailbox, else Immediate,
|
||||
when the configured mode waits for vsync and the surface offers one (`viewport_present_mode`).
|
||||
Waiting on both surfaces in one frame halved the frame rate, and on Wayland a window the
|
||||
compositor wasn't showing stalled the main window behind its present.
|
||||
- `src/lib.rs`, `src/winit.rs`: the device's lost callback marks `RenderState::device_lost`
|
||||
(`DeviceLost`), and the painter draws nothing on a lost device. Before a frame's uploads it
|
||||
submits nothing (`DeviceLost::probe`), so a device the driver lost is found by that
|
||||
|
||||
Vendored
+28
@@ -1058,6 +1058,34 @@ pub enum Attempt<E> {
|
||||
Refused(RetryPlan, E),
|
||||
}
|
||||
|
||||
/// SolidCraft: the present mode a viewport's surface is configured with. The main window keeps
|
||||
/// `configured`. Another window (the agent chat popped out into its own window) is an immediate
|
||||
/// viewport: eframe draws and presents it inside the main window's frame, one surface after the
|
||||
/// other. With vsync on both, every frame waited for the display twice, and on a compositor that
|
||||
/// sends no frame callbacks to a window it isn't showing (Wayland: on another workspace, scrolled
|
||||
/// away, covered) the main window stalled behind the other one's present. So another window
|
||||
/// presents without waiting where its surface can (Mailbox, which doesn't tear, before
|
||||
/// Immediate); the main window's vsync still paces both. A `configured` mode without vsync is
|
||||
/// kept everywhere.
|
||||
pub fn viewport_present_mode(
|
||||
root: bool,
|
||||
configured: wgpu::PresentMode,
|
||||
supported: &[wgpu::PresentMode],
|
||||
) -> wgpu::PresentMode {
|
||||
use wgpu::PresentMode;
|
||||
let waits = matches!(
|
||||
configured,
|
||||
PresentMode::AutoVsync | PresentMode::Fifo | PresentMode::FifoRelaxed
|
||||
);
|
||||
if root || !waits {
|
||||
return configured;
|
||||
}
|
||||
[PresentMode::Mailbox, PresentMode::Immediate]
|
||||
.into_iter()
|
||||
.find(|mode| supported.contains(mode))
|
||||
.unwrap_or(configured)
|
||||
}
|
||||
|
||||
/// SolidCraft: the surface size for a window of `width` x `height` pixels on a device whose
|
||||
/// textures are at most `max_side` on a side, and the scale it is drawn at. A window that fits is
|
||||
/// unchanged (scale 1); a larger one shrinks uniformly, keeping its aspect ratio, so the image
|
||||
|
||||
Vendored
+15
-1
@@ -111,6 +111,7 @@ impl Painter {
|
||||
/// device, or a surface the adapter doesn't support, neither of which configured it (#7007).
|
||||
/// Called only through [`Self::try_configure`], which says what follows.
|
||||
fn configure_surface(
|
||||
viewport_id: ViewportId,
|
||||
surface_state: &SurfaceState,
|
||||
render_state: &RenderState,
|
||||
config: &SurfaceConfig,
|
||||
@@ -141,6 +142,17 @@ impl Painter {
|
||||
else {
|
||||
return Err(crate::ConfigureError::Unsupported);
|
||||
};
|
||||
// SolidCraft: a window other than the main one presents without waiting for the display
|
||||
// where its surface can (`crate::viewport_present_mode`).
|
||||
let supported = surface_state
|
||||
.surface
|
||||
.get_capabilities(&render_state.adapter)
|
||||
.present_modes;
|
||||
let present_mode = crate::viewport_present_mode(
|
||||
viewport_id == ViewportId::ROOT,
|
||||
present_mode,
|
||||
&supported,
|
||||
);
|
||||
let mut surf_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: render_state.target_format,
|
||||
@@ -177,7 +189,9 @@ impl Painter {
|
||||
) -> bool {
|
||||
// A copy, as the configure borrows the whole surface state.
|
||||
let mut retry = state.retry;
|
||||
let attempt = retry.attempt(now, || Self::configure_surface(state, render_state, config));
|
||||
let attempt = retry.attempt(now, || {
|
||||
Self::configure_surface(viewport_id, state, render_state, config)
|
||||
});
|
||||
state.retry = retry;
|
||||
match attempt {
|
||||
crate::Attempt::Took(refusals) => {
|
||||
|
||||
Reference in New Issue
Block a user