mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 13:21:38 +03:00
try scaling down images that don't fit in the texture atlas
When trying to display a 4k image there is a high chance that we'll run out of texture space and then render with no images displayed. This commit changes the binary yes/no allow-images into a a series of attempts: display at natural size, scale down by 2, 4, then 8, then give up on images. While looking at this, I noticed that we had a TOCTOU in the blob lease stuff in the case where we might very quickly try the handle the same image in succession, and end up deleting a file out from under a live lease. I've put in place a simple bandaid for that, but it's probably worth revisiting the concurrency model for that.
This commit is contained in:
parent
134bf0a74d
commit
4ae176dd8a
@ -9,7 +9,7 @@ pub enum Error {
|
|||||||
#[error("Content with id {0} not found")]
|
#[error("Content with id {0} not found")]
|
||||||
ContentNotFound(ContentId),
|
ContentNotFound(ContentId),
|
||||||
|
|
||||||
#[error(transparent)]
|
#[error("Io error in BlobLease: {0}")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error("Storage has already been initialized")]
|
#[error("Storage has already been initialized")]
|
||||||
|
@ -44,6 +44,7 @@ impl SimpleTempDir {
|
|||||||
eprintln!("Failed to remove {}: {err:#}", path.display());
|
eprintln!("Failed to remove {}: {err:#}", path.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*count = 0;
|
||||||
}
|
}
|
||||||
Some(count) => {
|
Some(count) => {
|
||||||
*count -= 1;
|
*count -= 1;
|
||||||
@ -57,6 +58,8 @@ impl SimpleTempDir {
|
|||||||
|
|
||||||
impl BlobStorage for SimpleTempDir {
|
impl BlobStorage for SimpleTempDir {
|
||||||
fn store(&self, content_id: ContentId, data: &[u8], _lease_id: LeaseId) -> Result<(), Error> {
|
fn store(&self, content_id: ContentId, data: &[u8], _lease_id: LeaseId) -> Result<(), Error> {
|
||||||
|
let mut refs = self.refs.lock().unwrap();
|
||||||
|
|
||||||
let path = self.path_for_content(content_id)?;
|
let path = self.path_for_content(content_id)?;
|
||||||
let mut file = tempfile::Builder::new()
|
let mut file = tempfile::Builder::new()
|
||||||
.prefix("new-")
|
.prefix("new-")
|
||||||
@ -67,12 +70,14 @@ impl BlobStorage for SimpleTempDir {
|
|||||||
file.persist(&path)
|
file.persist(&path)
|
||||||
.map_err(|persist_err| persist_err.error)?;
|
.map_err(|persist_err| persist_err.error)?;
|
||||||
|
|
||||||
self.add_ref(content_id);
|
*refs.entry(content_id).or_insert(0) += 1;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_by_content(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<(), Error> {
|
fn lease_by_content(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<(), Error> {
|
||||||
|
let _refs = self.refs.lock().unwrap();
|
||||||
|
|
||||||
let path = self.path_for_content(content_id)?;
|
let path = self.path_for_content(content_id)?;
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
self.add_ref(content_id);
|
self.add_ref(content_id);
|
||||||
@ -83,6 +88,8 @@ impl BlobStorage for SimpleTempDir {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_data(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<Vec<u8>, Error> {
|
fn get_data(&self, content_id: ContentId, _lease_id: LeaseId) -> Result<Vec<u8>, Error> {
|
||||||
|
let _refs = self.refs.lock().unwrap();
|
||||||
|
|
||||||
let path = self.path_for_content(content_id)?;
|
let path = self.path_for_content(content_id)?;
|
||||||
Ok(std::fs::read(&path)?)
|
Ok(std::fs::read(&path)?)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use super::utilsprites::RenderMetrics;
|
use super::utilsprites::RenderMetrics;
|
||||||
use crate::customglyph::*;
|
use crate::customglyph::*;
|
||||||
use crate::renderstate::RenderContext;
|
use crate::renderstate::RenderContext;
|
||||||
|
use crate::termwindow::render::paint::AllowImage;
|
||||||
use ::window::bitmaps::atlas::{Atlas, OutOfTextureSpace, Sprite};
|
use ::window::bitmaps::atlas::{Atlas, OutOfTextureSpace, Sprite};
|
||||||
use ::window::bitmaps::{BitmapImage, Image, ImageTexture, Texture2d};
|
use ::window::bitmaps::{BitmapImage, Image, ImageTexture, Texture2d};
|
||||||
use ::window::color::SrgbaPixel;
|
use ::window::color::SrgbaPixel;
|
||||||
@ -885,17 +886,26 @@ impl GlyphCache {
|
|||||||
decoded: &DecodedImage,
|
decoded: &DecodedImage,
|
||||||
padding: Option<usize>,
|
padding: Option<usize>,
|
||||||
min_frame_duration: Duration,
|
min_frame_duration: Duration,
|
||||||
|
allow_image: AllowImage,
|
||||||
) -> anyhow::Result<(Sprite, Option<Instant>, LoadState)> {
|
) -> anyhow::Result<(Sprite, Option<Instant>, LoadState)> {
|
||||||
let mut handle = DecodedImageHandle {
|
let mut handle = DecodedImageHandle {
|
||||||
h: decoded.image.data(),
|
h: decoded.image.data(),
|
||||||
current_frame: *decoded.current_frame.borrow(),
|
current_frame: *decoded.current_frame.borrow(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let scale_down = match allow_image {
|
||||||
|
AllowImage::Scale(n) => Some(n),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
match &*handle.h {
|
match &*handle.h {
|
||||||
ImageDataType::Rgba8 { hash, .. } => {
|
ImageDataType::Rgba8 { hash, .. } => {
|
||||||
if let Some(sprite) = frame_cache.get(hash) {
|
if let Some(sprite) = frame_cache.get(hash) {
|
||||||
return Ok((sprite.clone(), None, LoadState::Loaded));
|
return Ok((sprite.clone(), None, LoadState::Loaded));
|
||||||
}
|
}
|
||||||
let sprite = atlas.allocate_with_padding(&handle, padding)?;
|
let sprite = atlas
|
||||||
|
.allocate_with_padding(&handle, padding, scale_down)
|
||||||
|
.context("atlas.allocate_with_padding")?;
|
||||||
frame_cache.insert(*hash, sprite.clone());
|
frame_cache.insert(*hash, sprite.clone());
|
||||||
|
|
||||||
return Ok((sprite, None, LoadState::Loaded));
|
return Ok((sprite, None, LoadState::Loaded));
|
||||||
@ -949,7 +959,9 @@ impl GlyphCache {
|
|||||||
return Ok((sprite.clone(), next, LoadState::Loaded));
|
return Ok((sprite.clone(), next, LoadState::Loaded));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sprite = atlas.allocate_with_padding(&handle, padding)?;
|
let sprite = atlas
|
||||||
|
.allocate_with_padding(&handle, padding, scale_down)
|
||||||
|
.context("atlas.allocate_with_padding")?;
|
||||||
|
|
||||||
frame_cache.insert(hash, sprite.clone());
|
frame_cache.insert(hash, sprite.clone());
|
||||||
|
|
||||||
@ -1005,9 +1017,13 @@ impl GlyphCache {
|
|||||||
let frame = Image::from_raw(
|
let frame = Image::from_raw(
|
||||||
frames.current_frame.width,
|
frames.current_frame.width,
|
||||||
frames.current_frame.height,
|
frames.current_frame.height,
|
||||||
frames.current_frame.lease.get_data()?,
|
frames
|
||||||
|
.current_frame
|
||||||
|
.lease
|
||||||
|
.get_data()
|
||||||
|
.context("frames.current_frame.lease.get_data")?,
|
||||||
);
|
);
|
||||||
let sprite = atlas.allocate_with_padding(&frame, padding)?;
|
let sprite = atlas.allocate_with_padding(&frame, padding, scale_down)?;
|
||||||
|
|
||||||
frame_cache.insert(hash, sprite.clone());
|
frame_cache.insert(hash, sprite.clone());
|
||||||
|
|
||||||
@ -1024,6 +1040,7 @@ impl GlyphCache {
|
|||||||
&mut self,
|
&mut self,
|
||||||
image_data: &Arc<ImageData>,
|
image_data: &Arc<ImageData>,
|
||||||
padding: Option<usize>,
|
padding: Option<usize>,
|
||||||
|
allow_image: AllowImage,
|
||||||
) -> anyhow::Result<(Sprite, Option<Instant>, LoadState)> {
|
) -> anyhow::Result<(Sprite, Option<Instant>, LoadState)> {
|
||||||
let hash = image_data.hash();
|
let hash = image_data.hash();
|
||||||
|
|
||||||
@ -1034,6 +1051,7 @@ impl GlyphCache {
|
|||||||
decoded,
|
decoded,
|
||||||
padding,
|
padding,
|
||||||
self.min_frame_duration,
|
self.min_frame_duration,
|
||||||
|
allow_image,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let decoded = DecodedImage::load(image_data);
|
let decoded = DecodedImage::load(image_data);
|
||||||
@ -1043,6 +1061,7 @@ impl GlyphCache {
|
|||||||
&decoded,
|
&decoded,
|
||||||
padding,
|
padding,
|
||||||
self.min_frame_duration,
|
self.min_frame_duration,
|
||||||
|
allow_image,
|
||||||
)?;
|
)?;
|
||||||
self.image_cache.put(hash, decoded);
|
self.image_cache.put(hash, decoded);
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
@ -425,10 +425,11 @@ impl crate::TermWindow {
|
|||||||
|
|
||||||
let color = bg_color.mul_alpha(layer.def.opacity);
|
let color = bg_color.mul_alpha(layer.def.opacity);
|
||||||
|
|
||||||
let (sprite, next_due, load_state) = gl_state
|
let (sprite, next_due, load_state) = gl_state.glyph_cache.borrow_mut().cached_image(
|
||||||
.glyph_cache
|
&layer.source,
|
||||||
.borrow_mut()
|
None,
|
||||||
.cached_image(&layer.source, None)?;
|
self.allow_images,
|
||||||
|
)?;
|
||||||
self.update_next_frame_time(next_due);
|
self.update_next_frame_time(next_due);
|
||||||
|
|
||||||
if load_state == LoadState::Loading {
|
if load_state == LoadState::Loading {
|
||||||
|
@ -19,6 +19,7 @@ use crate::termwindow::background::{
|
|||||||
};
|
};
|
||||||
use crate::termwindow::keyevent::{KeyTableArgs, KeyTableState};
|
use crate::termwindow::keyevent::{KeyTableArgs, KeyTableState};
|
||||||
use crate::termwindow::modal::Modal;
|
use crate::termwindow::modal::Modal;
|
||||||
|
use crate::termwindow::render::paint::AllowImage;
|
||||||
use crate::termwindow::render::{
|
use crate::termwindow::render::{
|
||||||
CachedLineState, LineQuadCacheKey, LineQuadCacheValue, LineToEleShapeCacheKey,
|
CachedLineState, LineQuadCacheKey, LineQuadCacheValue, LineToEleShapeCacheKey,
|
||||||
LineToElementShapeItem,
|
LineToElementShapeItem,
|
||||||
@ -73,7 +74,7 @@ mod mouseevent;
|
|||||||
pub mod palette;
|
pub mod palette;
|
||||||
pub mod paneselect;
|
pub mod paneselect;
|
||||||
mod prevcursor;
|
mod prevcursor;
|
||||||
mod render;
|
pub mod render;
|
||||||
pub mod resize;
|
pub mod resize;
|
||||||
mod selection;
|
mod selection;
|
||||||
pub mod spawn;
|
pub mod spawn;
|
||||||
@ -434,7 +435,7 @@ pub struct TermWindow {
|
|||||||
has_animation: RefCell<Option<Instant>>,
|
has_animation: RefCell<Option<Instant>>,
|
||||||
/// We use this to attempt to do something reasonable
|
/// We use this to attempt to do something reasonable
|
||||||
/// if we run out of texture space
|
/// if we run out of texture space
|
||||||
allow_images: bool,
|
allow_images: AllowImage,
|
||||||
scheduled_animation: RefCell<Option<Instant>>,
|
scheduled_animation: RefCell<Option<Instant>>,
|
||||||
|
|
||||||
created: Instant,
|
created: Instant,
|
||||||
@ -762,7 +763,7 @@ impl TermWindow {
|
|||||||
current_event: None,
|
current_event: None,
|
||||||
has_animation: RefCell::new(None),
|
has_animation: RefCell::new(None),
|
||||||
scheduled_animation: RefCell::new(None),
|
scheduled_animation: RefCell::new(None),
|
||||||
allow_images: true,
|
allow_images: AllowImage::Yes,
|
||||||
semantic_zones: HashMap::new(),
|
semantic_zones: HashMap::new(),
|
||||||
ui_items: vec![],
|
ui_items: vec![],
|
||||||
dragging: None,
|
dragging: None,
|
||||||
|
@ -6,11 +6,12 @@ use crate::quad::{
|
|||||||
TripleLayerQuadAllocatorTrait,
|
TripleLayerQuadAllocatorTrait,
|
||||||
};
|
};
|
||||||
use crate::shapecache::*;
|
use crate::shapecache::*;
|
||||||
|
use crate::termwindow::render::paint::AllowImage;
|
||||||
use crate::termwindow::{BorrowedShapeCacheKey, RenderState, ShapedInfo, TermWindowNotif};
|
use crate::termwindow::{BorrowedShapeCacheKey, RenderState, ShapedInfo, TermWindowNotif};
|
||||||
use crate::utilsprites::RenderMetrics;
|
use crate::utilsprites::RenderMetrics;
|
||||||
use ::window::bitmaps::{TextureCoord, TextureRect, TextureSize};
|
use ::window::bitmaps::{TextureCoord, TextureRect, TextureSize};
|
||||||
use ::window::{DeadKeyStatus, PointF, RectF, SizeF, WindowOps};
|
use ::window::{DeadKeyStatus, PointF, RectF, SizeF, WindowOps};
|
||||||
use anyhow::anyhow;
|
use anyhow::{anyhow, Context};
|
||||||
use config::{BoldBrightening, ConfigHandle, DimensionContext, TextStyle, VisualBellTarget};
|
use config::{BoldBrightening, ConfigHandle, DimensionContext, TextStyle, VisualBellTarget};
|
||||||
use euclid::num::Zero;
|
use euclid::num::Zero;
|
||||||
use mux::pane::{Pane, PaneId};
|
use mux::pane::{Pane, PaneId};
|
||||||
@ -417,7 +418,7 @@ impl crate::TermWindow {
|
|||||||
hsv: Option<config::HsbTransform>,
|
hsv: Option<config::HsbTransform>,
|
||||||
glyph_color: LinearRgba,
|
glyph_color: LinearRgba,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
if !self.allow_images {
|
if self.allow_images == AllowImage::No {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +436,8 @@ impl crate::TermWindow {
|
|||||||
let (sprite, next_due, _load_state) = gl_state
|
let (sprite, next_due, _load_state) = gl_state
|
||||||
.glyph_cache
|
.glyph_cache
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.cached_image(image.image_data(), Some(padding))?;
|
.cached_image(image.image_data(), Some(padding), self.allow_images)
|
||||||
|
.context("cached_image")?;
|
||||||
self.update_next_frame_time(next_due);
|
self.update_next_frame_time(next_due);
|
||||||
let width = sprite.coords.size.width;
|
let width = sprite.coords.size.width;
|
||||||
let height = sprite.coords.size.height;
|
let height = sprite.coords.size.height;
|
||||||
|
@ -6,6 +6,13 @@ use smol::Timer;
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use wezterm_font::ClearShapeCache;
|
use wezterm_font::ClearShapeCache;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum AllowImage {
|
||||||
|
Yes,
|
||||||
|
Scale(usize),
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
impl crate::TermWindow {
|
impl crate::TermWindow {
|
||||||
pub fn paint_impl(&mut self, frame: &mut RenderFrame) {
|
pub fn paint_impl(&mut self, frame: &mut RenderFrame) {
|
||||||
self.num_frames += 1;
|
self.num_frames += 1;
|
||||||
@ -13,7 +20,7 @@ impl crate::TermWindow {
|
|||||||
// invalidating as frequently
|
// invalidating as frequently
|
||||||
*self.has_animation.borrow_mut() = None;
|
*self.has_animation.borrow_mut() = None;
|
||||||
// Start with the assumption that we should allow images to render
|
// Start with the assumption that we should allow images to render
|
||||||
self.allow_images = true;
|
self.allow_images = AllowImage::Yes;
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
@ -61,21 +68,27 @@ impl crate::TermWindow {
|
|||||||
self.invalidate_modal();
|
self.invalidate_modal();
|
||||||
|
|
||||||
if let Err(err) = result {
|
if let Err(err) = result {
|
||||||
if self.allow_images {
|
self.allow_images = match self.allow_images {
|
||||||
self.allow_images = false;
|
AllowImage::Yes => AllowImage::Scale(2),
|
||||||
log::info!(
|
AllowImage::Scale(2) => AllowImage::Scale(4),
|
||||||
"Not enough texture space ({:#}); \
|
AllowImage::Scale(4) => AllowImage::Scale(8),
|
||||||
will retry render with images disabled",
|
AllowImage::Scale(8) => AllowImage::No,
|
||||||
err
|
AllowImage::No | _ => {
|
||||||
);
|
log::error!(
|
||||||
} else {
|
"Failed to {} texture: {}",
|
||||||
log::error!(
|
if pass == 0 { "clear" } else { "resize" },
|
||||||
"Failed to {} texture: {}",
|
err
|
||||||
if pass == 0 { "clear" } else { "resize" },
|
);
|
||||||
err
|
break 'pass;
|
||||||
);
|
}
|
||||||
break 'pass;
|
};
|
||||||
}
|
|
||||||
|
log::info!(
|
||||||
|
"Not enough texture space ({:#}); \
|
||||||
|
will retry render with {:?}",
|
||||||
|
err,
|
||||||
|
self.allow_images,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if err.root_cause().downcast_ref::<ClearShapeCache>().is_some() {
|
} else if err.root_cause().downcast_ref::<ClearShapeCache>().is_some() {
|
||||||
self.invalidate_fancy_tab_bar();
|
self.invalidate_fancy_tab_bar();
|
||||||
@ -175,7 +188,7 @@ impl crate::TermWindow {
|
|||||||
|
|
||||||
// Render the full window background
|
// Render the full window background
|
||||||
match (self.window_background.is_empty(), self.allow_images) {
|
match (self.window_background.is_empty(), self.allow_images) {
|
||||||
(false, true) => {
|
(false, AllowImage::Yes) => {
|
||||||
let bg_color = self.palette().background.to_linear();
|
let bg_color = self.palette().background.to_linear();
|
||||||
|
|
||||||
let top = panes
|
let top = panes
|
||||||
|
@ -8,6 +8,7 @@ use crate::termwindow::render::{
|
|||||||
use crate::termwindow::{ScrollHit, UIItem, UIItemType};
|
use crate::termwindow::{ScrollHit, UIItem, UIItemType};
|
||||||
use ::window::bitmaps::TextureRect;
|
use ::window::bitmaps::TextureRect;
|
||||||
use ::window::DeadKeyStatus;
|
use ::window::DeadKeyStatus;
|
||||||
|
use anyhow::Context;
|
||||||
use config::VisualBellTarget;
|
use config::VisualBellTarget;
|
||||||
use mux::pane::{PaneId, WithPaneLines};
|
use mux::pane::{PaneId, WithPaneLines};
|
||||||
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
use mux::renderable::{RenderableDimensions, StableCursorPosition};
|
||||||
@ -62,7 +63,8 @@ impl crate::TermWindow {
|
|||||||
let (padding_left, padding_top) = self.padding_left_top();
|
let (padding_left, padding_top) = self.padding_left_top();
|
||||||
|
|
||||||
let tab_bar_height = if self.show_tab_bar {
|
let tab_bar_height = if self.show_tab_bar {
|
||||||
self.tab_bar_pixel_height()?
|
self.tab_bar_pixel_height()
|
||||||
|
.context("tab_bar_pixel_height")?
|
||||||
} else {
|
} else {
|
||||||
0.
|
0.
|
||||||
};
|
};
|
||||||
@ -152,15 +154,17 @@ impl crate::TermWindow {
|
|||||||
if self.window_background.is_empty() {
|
if self.window_background.is_empty() {
|
||||||
// Per-pane, palette-specified background
|
// Per-pane, palette-specified background
|
||||||
|
|
||||||
let mut quad = self.filled_rectangle(
|
let mut quad = self
|
||||||
layers,
|
.filled_rectangle(
|
||||||
0,
|
layers,
|
||||||
background_rect,
|
0,
|
||||||
palette
|
background_rect,
|
||||||
.background
|
palette
|
||||||
.to_linear()
|
.background
|
||||||
.mul_alpha(config.window_background_opacity),
|
.to_linear()
|
||||||
)?;
|
.mul_alpha(config.window_background_opacity),
|
||||||
|
)
|
||||||
|
.context("filled_rectangle")?;
|
||||||
quad.set_hsv(if pos.is_active {
|
quad.set_hsv(if pos.is_active {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -205,7 +209,9 @@ impl crate::TermWindow {
|
|||||||
};
|
};
|
||||||
log::trace!("bell color is {:?}", background);
|
log::trace!("bell color is {:?}", background);
|
||||||
|
|
||||||
let mut quad = self.filled_rectangle(layers, 0, background_rect, background)?;
|
let mut quad = self
|
||||||
|
.filled_rectangle(layers, 0, background_rect, background)
|
||||||
|
.context("filled_rectangle")?;
|
||||||
|
|
||||||
quad.set_hsv(if pos.is_active {
|
quad.set_hsv(if pos.is_active {
|
||||||
None
|
None
|
||||||
@ -279,7 +285,8 @@ impl crate::TermWindow {
|
|||||||
thumb_size as f32,
|
thumb_size as f32,
|
||||||
),
|
),
|
||||||
color,
|
color,
|
||||||
)?;
|
)
|
||||||
|
.context("filled_rectangle")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (selrange, rectangular) = {
|
let (selrange, rectangular) = {
|
||||||
@ -451,7 +458,10 @@ impl crate::TermWindow {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
if !expired && !hover_changed {
|
if !expired && !hover_changed {
|
||||||
cached_quad.layers.apply_to(self.layers)?;
|
cached_quad
|
||||||
|
.layers
|
||||||
|
.apply_to(self.layers)
|
||||||
|
.context("cached_quad.layers.apply_to")?;
|
||||||
self.term_window.update_next_frame_time(cached_quad.expires);
|
self.term_window.update_next_frame_time(cached_quad.expires);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -476,49 +486,53 @@ impl crate::TermWindow {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let render_result = self.term_window.render_screen_line(
|
let render_result = self
|
||||||
RenderScreenLineParams {
|
.term_window
|
||||||
top_pixel_y: *quad_key.top_pixel_y,
|
.render_screen_line(
|
||||||
left_pixel_x: self.left_pixel_x,
|
RenderScreenLineParams {
|
||||||
pixel_width: self.dims.cols as f32
|
top_pixel_y: *quad_key.top_pixel_y,
|
||||||
* self.term_window.render_metrics.cell_size.width as f32,
|
left_pixel_x: self.left_pixel_x,
|
||||||
stable_line_idx: Some(stable_row),
|
pixel_width: self.dims.cols as f32
|
||||||
line: &line,
|
* self.term_window.render_metrics.cell_size.width as f32,
|
||||||
selection: selrange.clone(),
|
stable_line_idx: Some(stable_row),
|
||||||
cursor: &self.cursor,
|
line: &line,
|
||||||
palette: &self.palette,
|
selection: selrange.clone(),
|
||||||
dims: &self.dims,
|
cursor: &self.cursor,
|
||||||
config: &self.term_window.config,
|
palette: &self.palette,
|
||||||
cursor_border_color: self.cursor_border_color,
|
dims: &self.dims,
|
||||||
foreground: self.foreground,
|
config: &self.term_window.config,
|
||||||
is_active: self.pos.is_active,
|
cursor_border_color: self.cursor_border_color,
|
||||||
pane: Some(&self.pos.pane),
|
foreground: self.foreground,
|
||||||
selection_fg: self.selection_fg,
|
is_active: self.pos.is_active,
|
||||||
selection_bg: self.selection_bg,
|
pane: Some(&self.pos.pane),
|
||||||
cursor_fg: self.cursor_fg,
|
selection_fg: self.selection_fg,
|
||||||
cursor_bg: self.cursor_bg,
|
selection_bg: self.selection_bg,
|
||||||
cursor_is_default_color: self.cursor_is_default_color,
|
cursor_fg: self.cursor_fg,
|
||||||
white_space: self.white_space,
|
cursor_bg: self.cursor_bg,
|
||||||
filled_box: self.filled_box,
|
cursor_is_default_color: self.cursor_is_default_color,
|
||||||
window_is_transparent: self.window_is_transparent,
|
white_space: self.white_space,
|
||||||
default_bg: self.default_bg,
|
filled_box: self.filled_box,
|
||||||
font: None,
|
window_is_transparent: self.window_is_transparent,
|
||||||
style: None,
|
default_bg: self.default_bg,
|
||||||
use_pixel_positioning: self
|
font: None,
|
||||||
.term_window
|
style: None,
|
||||||
.config
|
use_pixel_positioning: self
|
||||||
.experimental_pixel_positioning,
|
.term_window
|
||||||
render_metrics: self.term_window.render_metrics,
|
.config
|
||||||
shape_key: Some(shape_key),
|
.experimental_pixel_positioning,
|
||||||
password_input,
|
render_metrics: self.term_window.render_metrics,
|
||||||
},
|
shape_key: Some(shape_key),
|
||||||
&mut TripleLayerQuadAllocator::Heap(&mut buf),
|
password_input,
|
||||||
)?;
|
},
|
||||||
|
&mut TripleLayerQuadAllocator::Heap(&mut buf),
|
||||||
|
)
|
||||||
|
.context("render_screen_line")?;
|
||||||
|
|
||||||
let expires = self.term_window.has_animation.borrow().as_ref().cloned();
|
let expires = self.term_window.has_animation.borrow().as_ref().cloned();
|
||||||
self.term_window.update_next_frame_time(next_due);
|
self.term_window.update_next_frame_time(next_due);
|
||||||
|
|
||||||
buf.apply_to(self.layers)?;
|
buf.apply_to(self.layers)
|
||||||
|
.context("HeapQuadAllocator::apply_to")?;
|
||||||
|
|
||||||
let quad_value = LineQuadCacheValue {
|
let quad_value = LineQuadCacheValue {
|
||||||
layers: buf,
|
layers: buf,
|
||||||
@ -554,7 +568,7 @@ impl crate::TermWindow {
|
|||||||
|
|
||||||
pos.pane.with_lines_mut(stable_range.clone(), &mut render);
|
pos.pane.with_lines_mut(stable_range.clone(), &mut render);
|
||||||
if let Some(error) = render.error.take() {
|
if let Some(error) = render.error.take() {
|
||||||
return Err(error);
|
return Err(error).context("error while calling with_lines_mut");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ use crate::termwindow::render::{
|
|||||||
};
|
};
|
||||||
use crate::termwindow::LineToElementShapeItem;
|
use crate::termwindow::LineToElementShapeItem;
|
||||||
use ::window::DeadKeyStatus;
|
use ::window::DeadKeyStatus;
|
||||||
|
use anyhow::Context;
|
||||||
use config::{HsbTransform, TextStyle};
|
use config::{HsbTransform, TextStyle};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -170,17 +171,19 @@ impl crate::TermWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if params.dims.reverse_video {
|
if params.dims.reverse_video {
|
||||||
let mut quad = self.filled_rectangle(
|
let mut quad = self
|
||||||
layers,
|
.filled_rectangle(
|
||||||
0,
|
layers,
|
||||||
euclid::rect(
|
0,
|
||||||
params.left_pixel_x,
|
euclid::rect(
|
||||||
params.top_pixel_y,
|
params.left_pixel_x,
|
||||||
params.pixel_width,
|
params.top_pixel_y,
|
||||||
cell_height,
|
params.pixel_width,
|
||||||
),
|
cell_height,
|
||||||
params.foreground,
|
),
|
||||||
)?;
|
params.foreground,
|
||||||
|
)
|
||||||
|
.context("filled_rectangle")?;
|
||||||
quad.set_hsv(hsv);
|
quad.set_hsv(hsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +251,9 @@ impl crate::TermWindow {
|
|||||||
|
|
||||||
let rect = euclid::rect(x, params.top_pixel_y, width, cell_height);
|
let rect = euclid::rect(x, params.top_pixel_y, width, cell_height);
|
||||||
if let Some(rect) = rect.intersection(&bounding_rect) {
|
if let Some(rect) = rect.intersection(&bounding_rect) {
|
||||||
let mut quad = self.filled_rectangle(layers, 0, rect, bg_color)?;
|
let mut quad = self
|
||||||
|
.filled_rectangle(layers, 0, rect, bg_color)
|
||||||
|
.context("filled_rectangle")?;
|
||||||
quad.set_hsv(hsv);
|
quad.set_hsv(hsv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,7 +263,7 @@ impl crate::TermWindow {
|
|||||||
// Draw one per cell, otherwise curly underlines
|
// Draw one per cell, otherwise curly underlines
|
||||||
// stretch across the whole span
|
// stretch across the whole span
|
||||||
for i in 0..cluster_width {
|
for i in 0..cluster_width {
|
||||||
let mut quad = layers.allocate(0)?;
|
let mut quad = layers.allocate(0).context("layers.allocate(0)")?;
|
||||||
let x = gl_x
|
let x = gl_x
|
||||||
+ params.left_pixel_x
|
+ params.left_pixel_x
|
||||||
+ if params.use_pixel_positioning {
|
+ if params.use_pixel_positioning {
|
||||||
@ -283,12 +288,14 @@ impl crate::TermWindow {
|
|||||||
let selection_pixel_range = if !params.selection.is_empty() {
|
let selection_pixel_range = if !params.selection.is_empty() {
|
||||||
let start = params.left_pixel_x + (params.selection.start as f32 * cell_width);
|
let start = params.left_pixel_x + (params.selection.start as f32 * cell_width);
|
||||||
let width = (params.selection.end - params.selection.start) as f32 * cell_width;
|
let width = (params.selection.end - params.selection.start) as f32 * cell_width;
|
||||||
let mut quad = self.filled_rectangle(
|
let mut quad = self
|
||||||
layers,
|
.filled_rectangle(
|
||||||
0,
|
layers,
|
||||||
euclid::rect(start, params.top_pixel_y, width, cell_height),
|
0,
|
||||||
params.selection_bg,
|
euclid::rect(start, params.top_pixel_y, width, cell_height),
|
||||||
)?;
|
params.selection_bg,
|
||||||
|
)
|
||||||
|
.context("filled_rectangle")?;
|
||||||
|
|
||||||
quad.set_hsv(hsv);
|
quad.set_hsv(hsv);
|
||||||
|
|
||||||
@ -343,7 +350,7 @@ impl crate::TermWindow {
|
|||||||
+ (phys(params.cursor.x, num_cols, direction) as f32 * cell_width);
|
+ (phys(params.cursor.x, num_cols, direction) as f32 * cell_width);
|
||||||
|
|
||||||
if cursor_shape.is_some() {
|
if cursor_shape.is_some() {
|
||||||
let mut quad = layers.allocate(0)?;
|
let mut quad = layers.allocate(0).context("layers.allocate(0)")?;
|
||||||
quad.set_hsv(hsv);
|
quad.set_hsv(hsv);
|
||||||
quad.set_has_color(false);
|
quad.set_has_color(false);
|
||||||
|
|
||||||
@ -355,13 +362,15 @@ impl crate::TermWindow {
|
|||||||
.map(|cell| cell.attrs().clone())
|
.map(|cell| cell.attrs().clone())
|
||||||
.unwrap_or_else(|| CellAttributes::blank());
|
.unwrap_or_else(|| CellAttributes::blank());
|
||||||
|
|
||||||
let glyph = self.resolve_lock_glyph(
|
let glyph = self
|
||||||
&TextStyle::default(),
|
.resolve_lock_glyph(
|
||||||
&attrs,
|
&TextStyle::default(),
|
||||||
params.font.as_ref(),
|
&attrs,
|
||||||
gl_state,
|
params.font.as_ref(),
|
||||||
¶ms.render_metrics,
|
gl_state,
|
||||||
)?;
|
¶ms.render_metrics,
|
||||||
|
)
|
||||||
|
.context("resolve_lock_glyph")?;
|
||||||
|
|
||||||
if let Some(sprite) = &glyph.texture {
|
if let Some(sprite) = &glyph.texture {
|
||||||
let width = sprite.coords.size.width as f32 * glyph.scale as f32;
|
let width = sprite.coords.size.width as f32 * glyph.scale as f32;
|
||||||
@ -486,7 +495,8 @@ impl crate::TermWindow {
|
|||||||
gl_state
|
gl_state
|
||||||
.glyph_cache
|
.glyph_cache
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.cached_block(*block, ¶ms.render_metrics)?,
|
.cached_block(*block, ¶ms.render_metrics)
|
||||||
|
.context("cached_block")?,
|
||||||
);
|
);
|
||||||
// Custom glyphs don't have the same offsets as computed
|
// Custom glyphs don't have the same offsets as computed
|
||||||
// by the shaper, and are rendered relative to the cell
|
// by the shaper, and are rendered relative to the cell
|
||||||
@ -624,7 +634,7 @@ impl crate::TermWindow {
|
|||||||
|
|
||||||
let texture_rect = texture.texture.to_texture_coords(pixel_rect);
|
let texture_rect = texture.texture.to_texture_coords(pixel_rect);
|
||||||
|
|
||||||
let mut quad = layers.allocate(1)?;
|
let mut quad = layers.allocate(1).context("layers.allocate(1)")?;
|
||||||
quad.set_position(
|
quad.set_position(
|
||||||
gl_x + range.start,
|
gl_x + range.start,
|
||||||
pos_y + top,
|
pos_y + top,
|
||||||
@ -691,7 +701,8 @@ impl crate::TermWindow {
|
|||||||
¶ms,
|
¶ms,
|
||||||
hsv,
|
hsv,
|
||||||
glyph_color,
|
glyph_color,
|
||||||
)?;
|
)
|
||||||
|
.context("populate_image_quad")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics::histogram!("render_screen_line", start.elapsed());
|
metrics::histogram!("render_screen_line", start.elapsed());
|
||||||
|
@ -56,16 +56,26 @@ impl Atlas {
|
|||||||
|
|
||||||
/// Reserve space for a sprite of the given size
|
/// Reserve space for a sprite of the given size
|
||||||
pub fn allocate(&mut self, im: &dyn BitmapImage) -> Result<Sprite, OutOfTextureSpace> {
|
pub fn allocate(&mut self, im: &dyn BitmapImage) -> Result<Sprite, OutOfTextureSpace> {
|
||||||
self.allocate_with_padding(im, None)
|
self.allocate_with_padding(im, None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate_with_padding(
|
pub fn allocate_with_padding(
|
||||||
&mut self,
|
&mut self,
|
||||||
im: &dyn BitmapImage,
|
im: &dyn BitmapImage,
|
||||||
padding: Option<usize>,
|
padding: Option<usize>,
|
||||||
|
scale_down: Option<usize>,
|
||||||
) -> Result<Sprite, OutOfTextureSpace> {
|
) -> Result<Sprite, OutOfTextureSpace> {
|
||||||
let (width, height) = im.image_dimensions();
|
let (width, height) = im.image_dimensions();
|
||||||
|
|
||||||
|
if let Some(scale_down) = scale_down {
|
||||||
|
let mut copied = crate::Image::new(width, height);
|
||||||
|
copied.draw_image(Point::new(0, 0), None, im);
|
||||||
|
|
||||||
|
let scaled = copied.resize(width / scale_down, height / scale_down);
|
||||||
|
|
||||||
|
return self.allocate_with_padding(&scaled, padding, None);
|
||||||
|
}
|
||||||
|
|
||||||
// If we can't convert the sizes to i32, then we'll never
|
// If we can't convert the sizes to i32, then we'll never
|
||||||
// be able to store this image
|
// be able to store this image
|
||||||
let reserve_width: i32 = width.try_into().map_err(|_| OutOfTextureSpace {
|
let reserve_width: i32 = width.try_into().map_err(|_| OutOfTextureSpace {
|
||||||
|
Loading…
Reference in New Issue
Block a user