Skip to content

Commit

Permalink
perf(chat-history): Render the shader directly to bubbles
Browse files Browse the repository at this point in the history
Rendering big texture into a lot of small bubbles results into bad
performance (30fps on my laptop while scrolling). Rendering the shader
to small bubbles fixes that issue.
  • Loading branch information
yuraiz committed Jun 11, 2023
1 parent f8df112 commit 810d769
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 47 deletions.
98 changes: 53 additions & 45 deletions src/session/content/background.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,20 @@ uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
uniform vec3 color4;
uniform vec2 p1;
uniform vec2 p2;
uniform vec2 p3;
uniform vec2 p4;
uniform vec4 p12;
uniform vec4 p34;
uniform vec4 gradient_bounds;
void mainImage(out vec4 fragColor,
in vec2 fragCoord,
in vec2 resolution,
in vec2 uv) {
vec2 p1 = p12.xy;
vec2 p2 = p12.zw;
vec2 p3 = p34.xy;
vec2 p4 = p34.zw;
uv = (fragCoord - gradient_bounds.xy) / gradient_bounds.zw;
uv.y = 1.0 - uv.y;
float dp1 = distance(uv, p1);
Expand Down Expand Up @@ -51,7 +56,6 @@ mod imp {
#[derive(Default)]
pub(crate) struct Background {
pub(super) background_texture: RefCell<Option<gdk::Texture>>,
pub(super) message_texture: RefCell<Option<gdk::Texture>>,

pub(super) last_size: Cell<(f32, f32)>,

Expand Down Expand Up @@ -178,17 +182,16 @@ mod imp {
let texture = match self.background_texture.take() {
Some(texture) if !size_changed => texture,
_ => {
self.render_textures(bounds);
self.background_texture.take().unwrap()
let renderer = self.obj().native().unwrap().renderer();

renderer.render_texture(self.obj().bg_node(bounds, bounds), Some(bounds))
}
};

snapshot.append_texture(&texture, bounds);
self.background_texture.replace(Some(texture));
} else {
self.render_textures(bounds);
let texture = self.background_texture.borrow().as_ref().unwrap().clone();
snapshot.append_texture(&texture, bounds);
snapshot.append_node(&self.obj().bg_node(bounds, bounds));
}
}

Expand Down Expand Up @@ -226,22 +229,10 @@ mod imp {
}
}

fn render_textures(&self, bounds: &graphene::Rect) {
let colors = [self.bg_colors.borrow(), self.message_colors.borrow()];

let renderer = self.obj().native().unwrap().renderer();

let mut textures = colors.into_iter().map(|colors| {
renderer.render_texture(self.gradient_shader_node(bounds, &colors), Some(bounds))
});

self.background_texture.replace(textures.next());
self.message_texture.replace(textures.next());
}

fn gradient_shader_node(
pub(super) fn gradient_shader_node(
&self,
bounds: &graphene::Rect,
gradient_bounds: &graphene::Rect,
colors: &[graphene::Vec3],
) -> gsk::GLShaderNode {
let Some(gradient_shader) = &*self.shader.borrow() else {
Expand All @@ -262,16 +253,25 @@ mod imp {
args_builder.set_vec3(2, &c3);
args_builder.set_vec3(3, &c4);

let [p1, p2, p3, p4] = Self::calculate_positions(progress, phase);
args_builder.set_vec2(4, &p1);
args_builder.set_vec2(5, &p2);
args_builder.set_vec2(6, &p3);
args_builder.set_vec2(7, &p4);
let [p12, p34] = Self::calculate_positions(progress, phase);
args_builder.set_vec4(4, &p12);
args_builder.set_vec4(5, &p34);

let gradient_bounds = {
graphene::Vec4::new(
gradient_bounds.x(),
gradient_bounds.y(),
gradient_bounds.width(),
gradient_bounds.height(),
)
};

args_builder.set_vec4(6, &gradient_bounds);

gsk::GLShaderNode::new(gradient_shader, bounds, &args_builder.to_args(), &[])
}

fn calculate_positions(progress: f32, phase: usize) -> [graphene::Vec2; 4] {
fn calculate_positions(progress: f32, phase: usize) -> [graphene::Vec4; 2] {
static POSITIONS: [(f32, f32); 8] = [
(0.80, 0.10),
(0.60, 0.20),
Expand All @@ -283,7 +283,7 @@ mod imp {
(0.75, 0.40),
];

let mut points = [graphene::Vec2::new(0.0, 0.0); 4];
let mut points = [(0.0, 0.0); 4];

for i in 0..4 {
let start = POSITIONS[(i * 2 + phase) % 8];
Expand All @@ -296,10 +296,18 @@ mod imp {
let x = interpolate(start.0, end.0, progress);
let y = interpolate(start.1, end.1, progress);

points[i] = graphene::Vec2::new(x, y);
points[i] = (x, y);
}

points
let points: Vec<_> = points
.chunks(2)
.map(|p| {
let [(x1, y1), (x2, y2)]: [(f32, f32); 2] = p.try_into().unwrap();
graphene::Vec4::from_float([x1, y1, x2, y2])
})
.collect();

points.try_into().unwrap()
}
}
}
Expand Down Expand Up @@ -350,22 +358,22 @@ impl Background {
animation.connect_value_notify(clone!(@weak child => move |_| child.queue_draw()));
}

pub fn bg_texture(&self) -> gdk::Texture {
pub fn bg_node(
&self,
bounds: &graphene::Rect,
gradient_bounds: &graphene::Rect,
) -> gsk::GLShaderNode {
self.imp()
.background_texture
.borrow()
.as_ref()
.unwrap()
.clone()
.gradient_shader_node(bounds, gradient_bounds, &*self.imp().bg_colors.borrow())
}

pub fn message_texture(&self) -> gdk::Texture {
pub fn message_bg_node(
&self,
bounds: &graphene::Rect,
gradient_bounds: &graphene::Rect,
) -> gsk::GLShaderNode {
self.imp()
.message_texture
.borrow()
.as_ref()
.unwrap()
.clone()
.gradient_shader_node(bounds, gradient_bounds, &*self.imp().bg_colors.borrow())
}

fn ensure_shader(&self) {
Expand Down
12 changes: 10 additions & 2 deletions src/session/content/message_row/bubble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,21 @@ mod imp {

if let Some(background) = self.parent_background.borrow().upgrade() {
if !background.has_css_class("fallback") {
let bounds = {
let width = widget.width() as f32;
let height = widget.height() as f32;

gtk::graphene::Rect::new(0.0, 0.0, width, height)
};

let gradient_bounds = background.compute_bounds(self.obj().as_ref()).unwrap();

if widget.has_css_class("outgoing") {
snapshot.append_texture(&background.message_texture(), &gradient_bounds);
snapshot
.append_node(&background.message_bg_node(&bounds, &gradient_bounds));
} else {
snapshot.push_opacity(0.1);
snapshot.append_texture(&background.bg_texture(), &gradient_bounds);
snapshot.append_node(&background.bg_node(&bounds, &gradient_bounds));
snapshot.pop();
};
}
Expand Down

0 comments on commit 810d769

Please sign in to comment.