mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 23:21:08 +03:00
render: 3 layers with dual source blending only for the glyph layer
This splits rendering into 3 passes: * background pass for z-index < 0. This is for solid background colors, background images, and image attachments with z-index < 0. Rendered with regular alpha blending. * glyph pass: for glyphs at z-index==0. This is rendered with dual source blending enabled to facilitate subpixel aa appearance. * top layer pass for z-index >= 0 graphics. This is rendered with regular alpha blending. This avoids weird effects, like images with alpha shining through the back of the window when the window itself isn't transparent. refs: #544
This commit is contained in:
parent
fc441e9879
commit
d38ba13211
@ -17,6 +17,7 @@ layout(location=0, index=1) out vec4 colorMask;
|
||||
uniform vec3 foreground_text_hsb;
|
||||
uniform sampler2D atlas_nearest_sampler;
|
||||
uniform sampler2D atlas_linear_sampler;
|
||||
uniform bool subpixel_aa;
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
@ -56,7 +57,11 @@ void main() {
|
||||
// The window background attachment
|
||||
color = texture(atlas_linear_sampler, o_tex);
|
||||
// Apply window_background_image_opacity to the background image
|
||||
colorMask = o_fg_color.aaaa;
|
||||
if (subpixel_aa) {
|
||||
colorMask = o_fg_color.aaaa;
|
||||
} else {
|
||||
color.a = o_fg_color.a;
|
||||
}
|
||||
} else if (o_has_color == 1.0) {
|
||||
// the texture is full color info (eg: color emoji glyph)
|
||||
color = texture(atlas_nearest_sampler, o_tex);
|
||||
@ -67,6 +72,9 @@ void main() {
|
||||
colorMask = texture(atlas_nearest_sampler, o_tex);
|
||||
// and we need to tint with the fg_color
|
||||
color = o_fg_color;
|
||||
if (!subpixel_aa) {
|
||||
color.a = colorMask.a;
|
||||
}
|
||||
color = apply_hsv(color, foreground_text_hsb);
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ pub struct RenderState {
|
||||
pub glyph_cache: RefCell<GlyphCache<SrgbTexture2d>>,
|
||||
pub util_sprites: UtilSprites<SrgbTexture2d>,
|
||||
pub glyph_prog: glium::Program,
|
||||
pub glyph_vertex_buffer: TripleVertexBuffer,
|
||||
pub vb: [TripleVertexBuffer; 3],
|
||||
}
|
||||
|
||||
impl RenderState {
|
||||
@ -125,14 +125,18 @@ impl RenderState {
|
||||
Ok(util_sprites) => {
|
||||
let glyph_prog = Self::compile_prog(&context, Self::glyph_shader)?;
|
||||
|
||||
let glyph_vertex_buffer = Self::compute_vertices(&context, 1024)?;
|
||||
let vb = [
|
||||
Self::compute_vertices(&context, 128)?,
|
||||
Self::compute_vertices(&context, 1024)?,
|
||||
Self::compute_vertices(&context, 128)?,
|
||||
];
|
||||
|
||||
return Ok(Self {
|
||||
context,
|
||||
glyph_cache,
|
||||
util_sprites,
|
||||
glyph_prog,
|
||||
glyph_vertex_buffer,
|
||||
vb,
|
||||
});
|
||||
}
|
||||
Err(OutOfTextureSpace {
|
||||
@ -179,10 +183,9 @@ impl RenderState {
|
||||
anyhow::bail!("Failed to compile shaders: {}", errors.join("\n"))
|
||||
}
|
||||
|
||||
pub fn reallocate_quads(&mut self, num_quads: usize) -> anyhow::Result<()> {
|
||||
let glyph_vertex_buffer = Self::compute_vertices(&self.context, num_quads)?;
|
||||
|
||||
self.glyph_vertex_buffer = glyph_vertex_buffer;
|
||||
pub fn reallocate_quads(&mut self, idx: usize, num_quads: usize) -> anyhow::Result<()> {
|
||||
let vb = Self::compute_vertices(&self.context, num_quads)?;
|
||||
self.vb[idx] = vb;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -95,28 +95,32 @@ impl super::TermWindow {
|
||||
|
||||
frame.clear_color(0., 0., 0., 0.);
|
||||
|
||||
for pass in 0.. {
|
||||
'pass: for pass in 0.. {
|
||||
match self.paint_opengl_pass() {
|
||||
Ok(_) => {
|
||||
let gl_state = self.render_state.as_mut().unwrap();
|
||||
if let Some(need_quads) = gl_state.glyph_vertex_buffer.need_more_quads() {
|
||||
// Round up to next multiple of 1024 that is >=
|
||||
// the number of needed quads for this frame
|
||||
let num_quads = (need_quads + 1023) & !1023;
|
||||
if let Err(err) = gl_state.reallocate_quads(num_quads) {
|
||||
log::error!(
|
||||
"Failed to allocate {} quads (needed {}): {:#}",
|
||||
num_quads,
|
||||
need_quads,
|
||||
err
|
||||
);
|
||||
break;
|
||||
let mut allocated = false;
|
||||
for vb_idx in 0..3 {
|
||||
if let Some(need_quads) = gl_state.vb[vb_idx].need_more_quads() {
|
||||
// Round up to next multiple of 1024 that is >=
|
||||
// the number of needed quads for this frame
|
||||
let num_quads = (need_quads + 1023) & !1023;
|
||||
if let Err(err) = gl_state.reallocate_quads(vb_idx, num_quads) {
|
||||
log::error!(
|
||||
"Failed to allocate {} quads (needed {}): {:#}",
|
||||
num_quads,
|
||||
need_quads,
|
||||
err
|
||||
);
|
||||
break 'pass;
|
||||
}
|
||||
log::trace!("Allocated {} quads (needed {})", num_quads, need_quads);
|
||||
allocated = true;
|
||||
}
|
||||
log::trace!("Allocated {} quads (needed {})", num_quads, need_quads);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
if !allocated {
|
||||
break 'pass;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if let Some(&OutOfTextureSpace {
|
||||
@ -148,14 +152,14 @@ impl super::TermWindow {
|
||||
if pass == 0 { "clear" } else { "resize" },
|
||||
err
|
||||
);
|
||||
break;
|
||||
break 'pass;
|
||||
}
|
||||
}
|
||||
} else if err.root_cause().downcast_ref::<ClearShapeCache>().is_some() {
|
||||
self.shape_cache.borrow_mut().clear();
|
||||
} else {
|
||||
log::error!("paint_opengl_pass failed: {:#}", err);
|
||||
break;
|
||||
break 'pass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,11 +270,17 @@ impl super::TermWindow {
|
||||
}
|
||||
|
||||
let gl_state = self.render_state.as_ref().unwrap();
|
||||
let vb = &gl_state.glyph_vertex_buffer;
|
||||
let vb = [&gl_state.vb[0], &gl_state.vb[1], &gl_state.vb[2]];
|
||||
|
||||
let start = Instant::now();
|
||||
let mut vb_mut = vb.current_vb_mut();
|
||||
let mut quads = vb.map(&mut vb_mut);
|
||||
let mut vb_mut0 = vb[0].current_vb_mut();
|
||||
let mut vb_mut1 = vb[1].current_vb_mut();
|
||||
let mut vb_mut2 = vb[2].current_vb_mut();
|
||||
let mut layers = [
|
||||
vb[0].map(&mut vb_mut0),
|
||||
vb[1].map(&mut vb_mut1),
|
||||
vb[2].map(&mut vb_mut2),
|
||||
];
|
||||
log::trace!("quad map elapsed {:?}", start.elapsed());
|
||||
metrics::histogram!("quad.map", start.elapsed());
|
||||
|
||||
@ -293,7 +303,7 @@ impl super::TermWindow {
|
||||
|
||||
// Render the full window background
|
||||
if pos.index == 0 {
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
quad.set_position(
|
||||
self.dimensions.pixel_width as f32 / -2.,
|
||||
self.dimensions.pixel_height as f32 / -2.,
|
||||
@ -339,7 +349,7 @@ impl super::TermWindow {
|
||||
}
|
||||
if num_panes > 1 && self.window_background.is_none() {
|
||||
// Per-pane, palette-specified background
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
let cell_width = self.render_metrics.cell_size.width as f32;
|
||||
let cell_height = self.render_metrics.cell_size.height as f32;
|
||||
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
|
||||
@ -425,7 +435,7 @@ impl super::TermWindow {
|
||||
window_is_transparent,
|
||||
default_bg,
|
||||
},
|
||||
&mut quads,
|
||||
&mut layers,
|
||||
)?;
|
||||
}
|
||||
|
||||
@ -440,7 +450,7 @@ impl super::TermWindow {
|
||||
let thumb_size = info.height as f32;
|
||||
let color = rgbcolor_to_window_color(palette.scrollbar_thumb);
|
||||
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[2].allocate()?;
|
||||
|
||||
// Adjust the scrollbar thumb position
|
||||
let top = (self.dimensions.pixel_height as f32 / -2.0) + thumb_top;
|
||||
@ -521,7 +531,7 @@ impl super::TermWindow {
|
||||
window_is_transparent,
|
||||
default_bg,
|
||||
},
|
||||
&mut quads,
|
||||
&mut layers,
|
||||
)?;
|
||||
}
|
||||
/*
|
||||
@ -533,7 +543,7 @@ impl super::TermWindow {
|
||||
log::trace!("lines elapsed {:?}", start.elapsed());
|
||||
|
||||
let start = Instant::now();
|
||||
drop(quads);
|
||||
drop(layers);
|
||||
metrics::histogram!("paint_pane_opengl.drop.quads", start.elapsed());
|
||||
log::trace!("quad drop elapsed {:?}", start.elapsed());
|
||||
|
||||
@ -542,9 +552,6 @@ impl super::TermWindow {
|
||||
|
||||
pub fn call_draw(&mut self, frame: &mut glium::Frame) -> anyhow::Result<()> {
|
||||
let gl_state = self.render_state.as_ref().unwrap();
|
||||
let vb = &gl_state.glyph_vertex_buffer;
|
||||
let (vertex_count, index_count) = vb.vertex_index_count();
|
||||
|
||||
let tex = gl_state.glyph_cache.borrow().atlas.texture();
|
||||
let projection = euclid::Transform3D::<f32, f32, f32>::ortho(
|
||||
-(self.dimensions.pixel_width as f32) / 2.0,
|
||||
@ -572,6 +579,21 @@ impl super::TermWindow {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let alpha_blending = glium::DrawParameters {
|
||||
blend: glium::Blend {
|
||||
color: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::SourceAlpha,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
alpha: BlendingFunction::Addition {
|
||||
source: LinearBlendingFactor::One,
|
||||
destination: LinearBlendingFactor::OneMinusSourceAlpha,
|
||||
},
|
||||
constant_value: (0.0, 0.0, 0.0, 0.0),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Clamp and use the nearest texel rather than interpolate.
|
||||
// This prevents things like the box cursor outlines from
|
||||
// being randomly doubled in width or height
|
||||
@ -592,22 +614,34 @@ impl super::TermWindow {
|
||||
foreground_text_hsb.brightness,
|
||||
);
|
||||
|
||||
let vertices = vb.current_vb();
|
||||
for idx in 0..3 {
|
||||
let vb = &gl_state.vb[idx];
|
||||
let (vertex_count, index_count) = vb.vertex_index_count();
|
||||
if vertex_count > 0 {
|
||||
let vertices = vb.current_vb();
|
||||
let subpixel_aa = idx == 1;
|
||||
|
||||
frame.draw(
|
||||
vertices.slice(0..vertex_count).unwrap(),
|
||||
vb.indices.slice(0..index_count).unwrap(),
|
||||
&gl_state.glyph_prog,
|
||||
&uniform! {
|
||||
projection: projection,
|
||||
atlas_nearest_sampler: atlas_nearest_sampler,
|
||||
atlas_linear_sampler: atlas_linear_sampler,
|
||||
foreground_text_hsb: foreground_text_hsb,
|
||||
},
|
||||
&dual_source_blending,
|
||||
)?;
|
||||
frame.draw(
|
||||
vertices.slice(0..vertex_count).unwrap(),
|
||||
vb.indices.slice(0..index_count).unwrap(),
|
||||
&gl_state.glyph_prog,
|
||||
&uniform! {
|
||||
projection: projection,
|
||||
atlas_nearest_sampler: atlas_nearest_sampler,
|
||||
atlas_linear_sampler: atlas_linear_sampler,
|
||||
foreground_text_hsb: foreground_text_hsb,
|
||||
subpixel_aa: subpixel_aa,
|
||||
},
|
||||
if subpixel_aa {
|
||||
&dual_source_blending
|
||||
} else {
|
||||
&alpha_blending
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
vb.next_index();
|
||||
vb.next_index();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -618,7 +652,7 @@ impl super::TermWindow {
|
||||
pane: &Rc<dyn Pane>,
|
||||
) -> anyhow::Result<()> {
|
||||
let gl_state = self.render_state.as_ref().unwrap();
|
||||
let vb = &gl_state.glyph_vertex_buffer;
|
||||
let vb = &gl_state.vb[2];
|
||||
let mut vb_mut = vb.current_vb_mut();
|
||||
let mut quads = vb.map(&mut vb_mut);
|
||||
let palette = pane.palette();
|
||||
@ -697,7 +731,9 @@ impl super::TermWindow {
|
||||
pub fn paint_opengl_pass(&mut self) -> anyhow::Result<()> {
|
||||
{
|
||||
let gl_state = self.render_state.as_ref().unwrap();
|
||||
gl_state.glyph_vertex_buffer.clear_quad_allocation();
|
||||
for vb in &gl_state.vb {
|
||||
vb.clear_quad_allocation();
|
||||
}
|
||||
}
|
||||
|
||||
// Clear out UI item positions; we'll rebuild these as we render
|
||||
@ -730,7 +766,7 @@ impl super::TermWindow {
|
||||
pub fn render_screen_line_opengl(
|
||||
&self,
|
||||
params: RenderScreenLineOpenGLParams,
|
||||
quads: &mut MappedQuads,
|
||||
layers: &mut [MappedQuads; 3],
|
||||
) -> anyhow::Result<()> {
|
||||
let gl_state = self.render_state.as_ref().unwrap();
|
||||
|
||||
@ -812,7 +848,7 @@ impl super::TermWindow {
|
||||
+ (cluster.first_cell_idx + params.pos.left) as f32 * cell_width
|
||||
+ self.config.window_padding.left as f32;
|
||||
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
quad.set_position(
|
||||
pos_x,
|
||||
pos_y,
|
||||
@ -829,7 +865,7 @@ impl super::TermWindow {
|
||||
|
||||
// Render the selection background color
|
||||
if !params.selection.is_empty() {
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
|
||||
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
|
||||
+ (params.selection.start + params.pos.left) as f32 * cell_width
|
||||
@ -1023,7 +1059,7 @@ impl super::TermWindow {
|
||||
|
||||
if bg_color != style_params.bg_color {
|
||||
// Override the background color
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
quad.set_position(
|
||||
pos_x,
|
||||
pos_y,
|
||||
@ -1038,7 +1074,7 @@ impl super::TermWindow {
|
||||
}
|
||||
|
||||
if cursor_shape.is_some() {
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[2].allocate()?;
|
||||
quad.set_position(
|
||||
pos_x,
|
||||
pos_y,
|
||||
@ -1072,7 +1108,7 @@ impl super::TermWindow {
|
||||
self.populate_image_quad(
|
||||
&img,
|
||||
gl_state,
|
||||
quads,
|
||||
&mut layers[0],
|
||||
cell_idx,
|
||||
¶ms,
|
||||
hsv,
|
||||
@ -1083,7 +1119,7 @@ impl super::TermWindow {
|
||||
|
||||
// Underlines
|
||||
if style_params.underline_tex_rect != params.white_space {
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
quad.set_position(pos_x, pos_y, pos_x + cell_width, pos_y + cell_height);
|
||||
quad.set_texture_adjust(0., 0., 0., 0.);
|
||||
quad.set_hsv(hsv);
|
||||
@ -1102,7 +1138,7 @@ impl super::TermWindow {
|
||||
self.populate_block_quad(
|
||||
block,
|
||||
gl_state,
|
||||
quads,
|
||||
&mut layers[0],
|
||||
cell_idx,
|
||||
¶ms,
|
||||
hsv,
|
||||
@ -1143,7 +1179,7 @@ impl super::TermWindow {
|
||||
slice_left = right;
|
||||
|
||||
if glyph_color != bg_color || glyph.has_color {
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[1].allocate()?;
|
||||
quad.set_position(
|
||||
pos_x,
|
||||
pos_y,
|
||||
@ -1178,7 +1214,15 @@ impl super::TermWindow {
|
||||
}
|
||||
|
||||
for (cell_idx, img, glyph_color) in overlay_images {
|
||||
self.populate_image_quad(&img, gl_state, quads, cell_idx, ¶ms, hsv, glyph_color)?;
|
||||
self.populate_image_quad(
|
||||
&img,
|
||||
gl_state,
|
||||
&mut layers[2],
|
||||
cell_idx,
|
||||
¶ms,
|
||||
hsv,
|
||||
glyph_color,
|
||||
)?;
|
||||
}
|
||||
|
||||
// If the clusters don't extend to the full physical width of the display,
|
||||
@ -1188,7 +1232,7 @@ impl super::TermWindow {
|
||||
let right_fill_start = Instant::now();
|
||||
if last_cell_idx < num_cols {
|
||||
if params.line.is_reverse() {
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[0].allocate()?;
|
||||
|
||||
let pos_x = (self.dimensions.pixel_width as f32 / -2.)
|
||||
+ (last_cell_idx + params.pos.left) as f32 * cell_width
|
||||
@ -1237,7 +1281,7 @@ impl super::TermWindow {
|
||||
|
||||
if bg_color != LinearRgba::TRANSPARENT {
|
||||
// Avoid poking a transparent hole underneath the cursor
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[2].allocate()?;
|
||||
quad.set_position(pos_x, pos_y, pos_x + cell_width, pos_y + cell_height);
|
||||
|
||||
quad.set_texture_adjust(0., 0., 0., 0.);
|
||||
@ -1247,7 +1291,7 @@ impl super::TermWindow {
|
||||
quad.set_fg_color(bg_color);
|
||||
}
|
||||
{
|
||||
let mut quad = quads.allocate()?;
|
||||
let mut quad = layers[2].allocate()?;
|
||||
quad.set_position(pos_x, pos_y, pos_x + cell_width, pos_y + cell_height);
|
||||
|
||||
quad.set_texture_adjust(0., 0., 0., 0.);
|
||||
|
Loading…
Reference in New Issue
Block a user