aboutsummaryrefslogtreecommitdiff
path: root/src/server/headless/render.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/headless/render.rs')
-rw-r--r--src/server/headless/render.rs169
1 files changed, 131 insertions, 38 deletions
diff --git a/src/server/headless/render.rs b/src/server/headless/render.rs
index a3bbedfa..5bc17c10 100644
--- a/src/server/headless/render.rs
+++ b/src/server/headless/render.rs
@@ -373,6 +373,15 @@ impl HeadlessServer {
}
pub(super) fn render_and_stream(&mut self) {
+ self.render_and_stream_with_graphics_limit(MAX_GRAPHICS_FRAME_SIZE);
+ }
+
+ #[cfg(all(test, unix))]
+ pub(super) fn render_and_stream_with_test_graphics_limit(&mut self, max: usize) {
+ self.render_and_stream_with_graphics_limit(max);
+ }
+
+ fn render_and_stream_with_graphics_limit(&mut self, graphics_frame_limit: usize) {
let full_started = crate::render_prof::timer();
let render_targets = render_targets(&self.clients, self.foreground_client_id);
@@ -665,8 +674,16 @@ impl HeadlessServer {
popup,
graphics,
graphics_delivery: next_graphics_delivery,
+ graphics_sources,
} = shell_render.expect("active shell surface");
- surface_parts = Some((panes, splits, popup, graphics, next_graphics_delivery));
+ surface_parts = Some((
+ panes,
+ splits,
+ popup,
+ graphics,
+ next_graphics_delivery,
+ graphics_sources,
+ ));
frame
}
ClientConnectionMode::TerminalPending => continue,
@@ -725,6 +742,20 @@ impl HeadlessServer {
}
};
+ if surface_parts
+ .as_ref()
+ .is_some_and(|(_, _, _, graphics, _, _)| {
+ self.defer_changed_native_geometry(client_id, graphics)
+ })
+ {
+ continue;
+ }
+ let mut native_upload =
+ surface_parts
+ .as_mut()
+ .and_then(|(_, _, _, graphics, delivery, sources)| {
+ self.prepare_native_scene(client_id, graphics, delivery, sources)
+ });
let Some(client) = self.clients.get_mut(&client_id) else {
continue;
};
@@ -734,62 +765,102 @@ impl HeadlessServer {
};
let has_graphics = surface_parts
.as_ref()
- .is_some_and(|(_, _, _, graphics, _)| {
+ .is_some_and(|(_, _, _, graphics, _, _)| {
!graphics.assets.is_empty()
|| !graphics.placements.is_empty()
|| !graphics.retained_assets.is_empty()
});
let mut next_shell_graphics_delivery = None;
- let prepared = if let Some((panes, splits, popup, graphics, delivery)) = surface_parts {
- next_shell_graphics_delivery = Some(delivery);
- client
- .render_state
- .prepare_pane_surface(protocol::PaneSurfaceFrame {
- boot_id: self.client_shell_boot_id.clone(),
- projection_revision: shell_projection_revision,
- surface_revision: 0,
- frame,
- panes,
- splits,
- popup,
- graphics,
- })
- } else {
- client.render_state.prepare_frame(frame)
- };
+ let prepared =
+ if let Some((panes, splits, popup, graphics, delivery, _)) = surface_parts {
+ next_shell_graphics_delivery = Some(delivery);
+ client.render_state.prepare_pane_surface_with_file(
+ protocol::PaneSurfaceFrame {
+ boot_id: self.client_shell_boot_id.clone(),
+ projection_revision: shell_projection_revision,
+ surface_revision: 0,
+ frame,
+ panes,
+ splits,
+ popup,
+ graphics,
+ },
+ native_upload.is_some(),
+ )
+ } else {
+ client.render_state.prepare_frame(frame)
+ };
let Some(mut prepared) = prepared else {
client.clear_deferred_render();
crate::render_prof::event("full_render.skip_identical");
continue;
};
let max = if has_graphics {
- MAX_GRAPHICS_FRAME_SIZE
+ graphics_frame_limit
} else {
crate::protocol::MAX_FRAME_SIZE
};
let mut shell_assets_deferred = false;
- let serialized = match Self::frame_server_message_with_max(prepared.message(), max) {
+ let mut suppress_impossible_asset_retry = false;
+ let mut stripped_assets = Vec::new();
+ let mut serialized = match Self::frame_server_message_with_max(prepared.message(), max)
+ {
Ok(frame) => frame,
Err(protocol::FramingError::Oversized { claimed, max }) if has_graphics => {
warn!(
client_id,
- claimed, max, "dropping graphics assets from oversized pane surface"
+ claimed, max, "trimming inline graphics assets from oversized pane surface"
);
- if !prepared.strip_pane_surface_assets() {
- crate::render_prof::event("full_render.serialize_oversized");
+ let framed = loop {
+ let Some(key) = prepared.pop_pane_surface_asset() else {
+ break None;
+ };
+ stripped_assets.push(key);
+ match Self::frame_server_message_with_max(prepared.message(), max) {
+ Ok(framed) => break Some(framed),
+ Err(protocol::FramingError::Oversized { .. }) => {}
+ Err(err) => {
+ warn!(client_id, err = %err, "failed to serialize trimmed pane surface");
+ broken_clients.push(client_id);
+ break None;
+ }
+ }
+ };
+ let Some(framed) = framed else {
+ if stripped_assets.is_empty() && prepared.has_queued_surface_assets() {
+ // Delta/reuse owns an encoded payload that cannot be trimmed in
+ // place. Drop its baseline so the bounded full-surface path runs next.
+ client.render_state.request_repaint();
+ client.defer_full_render();
+ } else {
+ crate::render_prof::event("full_render.serialize_oversized");
+ }
continue;
- }
- next_shell_graphics_delivery = None;
- shell_assets_deferred = true;
- match Self::frame_server_message(prepared.message()) {
- Ok(framed) => framed,
- Err(err) => {
- warn!(client_id, err = %err, "failed to serialize pane surface without assets");
- broken_clients.push(client_id);
- crate::render_prof::event("full_render.serialize_error");
- continue;
+ };
+ let made_progress =
+ native_upload.is_some() || prepared.has_queued_surface_assets();
+ if let Some(delivery) = next_shell_graphics_delivery.as_mut() {
+ for key in &stripped_assets {
+ delivery.forget_asset(key);
}
+ shell_assets_deferred = made_progress && !stripped_assets.is_empty();
+ }
+ if let (Some((pending, _)), Some(delivery)) = (
+ native_upload.as_mut(),
+ next_shell_graphics_delivery.as_ref(),
+ ) {
+ pending.defer_inline_delivery(delivery);
}
+ if !made_progress {
+ // Keep the asset absent from the delivery cache, but do not spin on
+ // an identical frame when no payload can fit beside its metadata.
+ suppress_impossible_asset_retry = true;
+ warn!(
+ client_id,
+ "inline graphics asset cannot fit in a graphics frame; waiting for a later scene change"
+ );
+ }
+ framed
}
Err(protocol::FramingError::Oversized { claimed, max }) => {
warn!(
@@ -806,11 +877,33 @@ impl HeadlessServer {
continue;
}
};
- let shell_graphics_pending = next_shell_graphics_delivery
- .as_ref()
- .is_some_and(crate::kitty_graphics::surface::DeliveryCache::has_pending);
- match writer.render.try_send(serialized) {
+ let shell_graphics_pending = !suppress_impossible_asset_retry
+ && next_shell_graphics_delivery
+ .as_ref()
+ .is_some_and(crate::kitty_graphics::surface::DeliveryCache::has_pending);
+ if let Some((_, message)) = &native_upload {
+ let Ok(file_frame) =
+ Self::frame_server_message_with_max(message, MAX_GRAPHICS_FRAME_SIZE)
+ else {
+ client.defer_full_render();
+ continue;
+ };
+ serialized.extend_from_slice(&file_frame);
+ }
+ let send = if native_upload.is_some() || self.native_graphics.is_pending(client_id) {
+ writer.render.send_ordered(serialized)
+ } else {
+ writer.render.try_send(serialized)
+ };
+ match send {
Ok(()) => {
+ if let Some((graphics, inline_assets)) = prepared.queued_surface_graphics() {
+ self.native_graphics
+ .commit_scene(client_id, graphics, inline_assets);
+ }
+ if let Some((pending, _)) = native_upload {
+ self.native_graphics.commit(client_id, pending);
+ }
if let Some(delivery) = next_shell_graphics_delivery {
client.shell_graphics_delivery = delivery;
}