|
|
@@ -65,7 +65,7 @@ struct Message {
|
|
|
}
|
|
|
|
|
|
const LINES_PER_PAGE: usize = 10;
|
|
|
-const PRELOAD_PAGES: usize = 200;
|
|
|
+const PRELOAD_PAGES: usize = 10;
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
struct Page {
|
|
|
@@ -73,6 +73,122 @@ struct Page {
|
|
|
atlas: text2::RenderedAtlas,
|
|
|
}
|
|
|
|
|
|
+#[derive(Clone)]
|
|
|
+struct PageMeshInfo {
|
|
|
+ px_height: f32,
|
|
|
+ mesh: DrawMesh,
|
|
|
+}
|
|
|
+
|
|
|
+type Page2Ptr = Arc<Page2>;
|
|
|
+
|
|
|
+struct Page2 {
|
|
|
+ msgs: Vec<Message>,
|
|
|
+ atlas: text2::RenderedAtlas,
|
|
|
+ // One draw call per page.
|
|
|
+ // Resizing the canvas means we recalc wrapping and the mesh changes
|
|
|
+ mesh_inf: SyncMutex<Option<PageMeshInfo>>,
|
|
|
+}
|
|
|
+
|
|
|
+impl Page2 {
|
|
|
+ async fn new(msgs: Vec<Message>, render_api: &RenderApi) -> Arc<Self> {
|
|
|
+ let mut atlas = text2::Atlas::new(render_api);
|
|
|
+ for msg in &msgs {
|
|
|
+ atlas.push(&msg.glyphs);
|
|
|
+ }
|
|
|
+ let Ok(atlas) = atlas.make().await else {
|
|
|
+ // what else should I do here?
|
|
|
+ panic!("unable to make atlas!");
|
|
|
+ };
|
|
|
+
|
|
|
+ Arc::new(Self { msgs, atlas, mesh_inf: SyncMutex::new(None) })
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Regenerates the mesh, returning the old mesh which should be freed
|
|
|
+ async fn regen_mesh(
|
|
|
+ &self,
|
|
|
+ clip: &Rectangle,
|
|
|
+ render_api: &RenderApi,
|
|
|
+ font_size: f32,
|
|
|
+ line_height: f32,
|
|
|
+ baseline: f32,
|
|
|
+ nick_colors: &[Color],
|
|
|
+ timestamp_color: Color,
|
|
|
+ text_color: Color,
|
|
|
+ ) -> (PageMeshInfo, Option<DrawMesh>) {
|
|
|
+ // Nudge the bottom line up slightly, otherwise chars like p will cross the bottom.
|
|
|
+ //let descent = line_height - baseline;
|
|
|
+
|
|
|
+ let mut wrapped_line_idx = 0;
|
|
|
+
|
|
|
+ let mut mesh = MeshBuilder::new();
|
|
|
+
|
|
|
+ for msg in &self.msgs {
|
|
|
+ let glyphs = &msg.glyphs;
|
|
|
+
|
|
|
+ let nick_color = select_nick_color(&msg.chatmsg.nick, nick_colors);
|
|
|
+
|
|
|
+ // Keep track of the 'section'
|
|
|
+ // Section 0 is the timestamp
|
|
|
+ // Section 1 is the nickname (colorized)
|
|
|
+ // Finally is just the message itself
|
|
|
+ let mut section = 2;
|
|
|
+
|
|
|
+ let mut lines = text2::wrap(clip.w, font_size, glyphs);
|
|
|
+ // We are drawing bottom up but line wrap gives us lines in normal order
|
|
|
+ lines.reverse();
|
|
|
+ let last_idx = lines.len() - 1;
|
|
|
+ for (i, line) in lines.into_iter().enumerate() {
|
|
|
+ let off_y = wrapped_line_idx as f32 * line_height;
|
|
|
+
|
|
|
+ if i == last_idx {
|
|
|
+ section = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Render line
|
|
|
+ let mut glyph_pos_iter = GlyphPositionIter::new(font_size, &line, baseline);
|
|
|
+ for (mut glyph_rect, glyph) in glyph_pos_iter.zip(line.iter()) {
|
|
|
+ let uv_rect =
|
|
|
+ self.atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect");
|
|
|
+ glyph_rect.y -= off_y;
|
|
|
+
|
|
|
+ let color = match section {
|
|
|
+ 0 => timestamp_color,
|
|
|
+ 1 => nick_color,
|
|
|
+ _ => text_color,
|
|
|
+ };
|
|
|
+
|
|
|
+ mesh.draw_box(&glyph_rect, color, uv_rect);
|
|
|
+
|
|
|
+ if section < 2 && is_whitespace(&glyph.substr) {
|
|
|
+ section += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ wrapped_line_idx += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let px_height = wrapped_line_idx as f32 * line_height;
|
|
|
+
|
|
|
+ let mesh = mesh.alloc(render_api).await.unwrap();
|
|
|
+ let mesh = mesh.draw_with_texture(self.atlas.texture_id);
|
|
|
+
|
|
|
+ let mesh_inf = PageMeshInfo { px_height, mesh };
|
|
|
+
|
|
|
+ let old = std::mem::replace(&mut *self.mesh_inf.lock().unwrap(), Some(mesh_inf.clone()));
|
|
|
+ let old = old.map(|v| v.mesh);
|
|
|
+
|
|
|
+ (mesh_inf, old)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn select_nick_color(nick: &str, nick_colors: &[Color]) -> Color {
|
|
|
+ let mut hasher = DefaultHasher::new();
|
|
|
+ nick.hash(&mut hasher);
|
|
|
+ let i = hasher.finish() as usize;
|
|
|
+ let color = nick_colors[i % nick_colors.len()];
|
|
|
+ color
|
|
|
+}
|
|
|
+
|
|
|
struct TouchInfo {
|
|
|
start_scroll: f32,
|
|
|
start_y: f32,
|
|
|
@@ -97,6 +213,7 @@ pub struct ChatView {
|
|
|
tree: sled::Tree,
|
|
|
|
|
|
pages: SyncMutex<Vec<Page>>,
|
|
|
+ pages2: SyncMutex<Vec<Page2Ptr>>,
|
|
|
drawcalls: SyncMutex<Vec<DrawMesh>>,
|
|
|
dc_key: u64,
|
|
|
|
|
|
@@ -189,6 +306,7 @@ impl ChatView {
|
|
|
tree,
|
|
|
|
|
|
pages: SyncMutex::new(Vec::new()),
|
|
|
+ pages2: SyncMutex::new(Vec::new()),
|
|
|
drawcalls: SyncMutex::new(Vec::new()),
|
|
|
dc_key: OsRng.gen(),
|
|
|
|
|
|
@@ -207,7 +325,9 @@ impl ChatView {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+ let timer = std::time::Instant::now();
|
|
|
self_.populate().await;
|
|
|
+ debug!(target: "ui::chatview", "populate() took {:?}", timer.elapsed());
|
|
|
|
|
|
Pimpl::ChatView(self_)
|
|
|
}
|
|
|
@@ -261,10 +381,10 @@ impl ChatView {
|
|
|
|
|
|
let mouse_pos = self.mouse_pos.lock().unwrap().clone();
|
|
|
if !rect.contains(&mouse_pos) {
|
|
|
- debug!(target: "ui::chatview", "not inside rect");
|
|
|
+ //debug!(target: "ui::chatview", "not inside rect");
|
|
|
return
|
|
|
}
|
|
|
- debug!(target: "ui::chatview", "inside rect");
|
|
|
+ //debug!(target: "ui::chatview", "inside rect");
|
|
|
let mut scroll = self.scroll.get() + wheel_y * 50.;
|
|
|
if scroll < 0. {
|
|
|
scroll = 0.;
|
|
|
@@ -353,6 +473,8 @@ impl ChatView {
|
|
|
|
|
|
async fn populate(&self) {
|
|
|
let mut pages = vec![];
|
|
|
+ let mut pages_len = 0;
|
|
|
+
|
|
|
let mut msgs = vec![];
|
|
|
|
|
|
for entry in self.tree.iter().rev() {
|
|
|
@@ -374,19 +496,12 @@ impl ChatView {
|
|
|
msgs.push(Message { timest, chatmsg, glyphs });
|
|
|
|
|
|
if msgs.len() >= LINES_PER_PAGE {
|
|
|
- let mut atlas = text2::Atlas::new(&self.render_api);
|
|
|
- for msg in &msgs {
|
|
|
- atlas.push(&msg.glyphs);
|
|
|
- }
|
|
|
- let Ok(atlas) = atlas.make().await else {
|
|
|
- // what else should I do here?
|
|
|
- panic!("unable to make atlas!");
|
|
|
- };
|
|
|
+ let page = Page2::new(msgs.clone(), &self.render_api).await;
|
|
|
|
|
|
- let page = Page { msgs: std::mem::take(&mut msgs), atlas };
|
|
|
- pages.push(page);
|
|
|
+ self.pages2.lock().unwrap().push(page);
|
|
|
+ pages_len += 1;
|
|
|
|
|
|
- if pages.len() >= PRELOAD_PAGES {
|
|
|
+ if pages_len >= PRELOAD_PAGES {
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
@@ -395,6 +510,69 @@ impl ChatView {
|
|
|
*self.pages.lock().unwrap() = pages;
|
|
|
}
|
|
|
|
|
|
+ async fn draw_cached(&self, mut rect: Rectangle) -> Vec<DrawInstruction> {
|
|
|
+ let font_size = self.font_size.get();
|
|
|
+ let line_height = self.line_height.get();
|
|
|
+ let baseline = self.baseline.get();
|
|
|
+
|
|
|
+ let mut instrs = vec![];
|
|
|
+
|
|
|
+ let timest_color = self.timestamp_color.get();
|
|
|
+ let text_color = self.text_color.get();
|
|
|
+ let nick_colors = self.read_nick_colors();
|
|
|
+
|
|
|
+ // Nudge the bottom line up slightly, otherwise chars like p will cross the bottom.
|
|
|
+ let descent = line_height - baseline;
|
|
|
+
|
|
|
+ let pages = self.pages2.lock().unwrap().clone();
|
|
|
+ let mut current_height = descent;
|
|
|
+ for page in pages {
|
|
|
+ if current_height > rect.h {
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ let mesh_inf = page.mesh_inf.lock().unwrap().clone();
|
|
|
+ let mesh_inf = match mesh_inf {
|
|
|
+ Some(mesh_inf) => mesh_inf,
|
|
|
+ None => {
|
|
|
+ let (mesh_inf, old_drawmesh) = page
|
|
|
+ .regen_mesh(
|
|
|
+ &rect,
|
|
|
+ &self.render_api,
|
|
|
+ font_size,
|
|
|
+ line_height,
|
|
|
+ baseline,
|
|
|
+ &nick_colors,
|
|
|
+ timest_color.clone(),
|
|
|
+ text_color.clone(),
|
|
|
+ )
|
|
|
+ .await;
|
|
|
+ assert!(old_drawmesh.is_none());
|
|
|
+ mesh_inf
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // Apply scroll and scissor
|
|
|
+ // We use the scissor for scrolling
|
|
|
+ // Because we use the scissor, our actual rect is now rect instead of parent_rect
|
|
|
+ let off_x = 0.;
|
|
|
+ // This calc decides whether scroll is in terms of pages or pixels
|
|
|
+ let off_y = (self.scroll.get() - current_height + rect.h) / rect.h;
|
|
|
+ let scale_x = 1. / rect.w;
|
|
|
+ let scale_y = 1. / rect.h;
|
|
|
+ let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
|
|
|
+ glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
|
|
|
+
|
|
|
+ instrs.push(DrawInstruction::ApplyMatrix(model));
|
|
|
+
|
|
|
+ instrs.push(DrawInstruction::Draw(mesh_inf.mesh));
|
|
|
+
|
|
|
+ current_height += mesh_inf.px_height;
|
|
|
+ }
|
|
|
+
|
|
|
+ instrs
|
|
|
+ }
|
|
|
+
|
|
|
/// Basically a version of redraw() where regen_mesh() is never called.
|
|
|
/// Instead we use the cached version.
|
|
|
async fn scrollview(&self) {
|
|
|
@@ -414,34 +592,14 @@ impl ChatView {
|
|
|
panic!("Node {:?} bad rect property", node);
|
|
|
};
|
|
|
|
|
|
- //let mut drawcalls = self.regen_mesh(rect.clone()).await;
|
|
|
-
|
|
|
- debug!(target: "ui::chatview", "chatview rect = {:?}", rect);
|
|
|
-
|
|
|
- // Apply scroll and scissor
|
|
|
- // We use the scissor for scrolling
|
|
|
- // Because we use the scissor, our actual rect is now rect instead of parent_rect
|
|
|
- let off_x = 0.;
|
|
|
- // This calc decides whether scroll is in terms of pages or pixels
|
|
|
- let off_y = (self.scroll.get() + rect.h) / rect.h;
|
|
|
- let scale_x = 1. / rect.w;
|
|
|
- let scale_y = 1. / rect.h;
|
|
|
- let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
|
|
|
- glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
|
|
|
-
|
|
|
- let mut instrs =
|
|
|
- vec![DrawInstruction::ApplyViewport(rect), DrawInstruction::ApplyMatrix(model)];
|
|
|
- let drawcalls = self.drawcalls.lock().unwrap().clone();
|
|
|
- let mut drawcalls: Vec<_> =
|
|
|
- drawcalls.into_iter().map(|dc| DrawInstruction::Draw(dc)).collect();
|
|
|
- instrs.append(&mut drawcalls);
|
|
|
+ let mut mesh_instrs = self.draw_cached(rect.clone()).await;
|
|
|
+ let mut instrs = vec![DrawInstruction::ApplyViewport(rect)];
|
|
|
+ instrs.append(&mut mesh_instrs);
|
|
|
|
|
|
let draw_calls =
|
|
|
vec![(self.dc_key, DrawCall { instrs, dcs: vec![], z_index: self.z_index.get() })];
|
|
|
|
|
|
self.render_api.replace_draw_calls(draw_calls).await;
|
|
|
-
|
|
|
- debug!(target: "ui::chatview", "scrollview done");
|
|
|
}
|
|
|
|
|
|
async fn redraw(&self) {
|
|
|
@@ -467,91 +625,62 @@ impl ChatView {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async fn regen_mesh(&self, mut clip: Rectangle) -> Vec<DrawMesh> {
|
|
|
+ async fn regen_mesh(&self, mut rect: Rectangle) -> (Vec<DrawInstruction>, Vec<DrawMesh>) {
|
|
|
let font_size = self.font_size.get();
|
|
|
let line_height = self.line_height.get();
|
|
|
let baseline = self.baseline.get();
|
|
|
- // Draw time and nick, then go over each word. If word crosses end of line
|
|
|
- // then apply a line break before the word and continue.
|
|
|
- let pages = self.pages.lock().unwrap().clone();
|
|
|
|
|
|
- let mut draws = vec![];
|
|
|
- let color = COLOR_WHITE;
|
|
|
+ let mut instrs = vec![];
|
|
|
+ let mut old_drawmesh = vec![];
|
|
|
|
|
|
- let timestamp_color = self.timestamp_color.get();
|
|
|
+ let timest_color = self.timestamp_color.get();
|
|
|
let text_color = self.text_color.get();
|
|
|
let nick_colors = self.read_nick_colors();
|
|
|
|
|
|
- // This is a little hack to nudge the bottom line up slightly, otherwise
|
|
|
- // chars like p will cross the bottom.
|
|
|
- let descent = baseline / 2.;
|
|
|
+ // Nudge the bottom line up slightly, otherwise chars like p will cross the bottom.
|
|
|
+ let descent = line_height - baseline;
|
|
|
|
|
|
- // Pages start at the bottom.
|
|
|
- let mut current_idx = 0;
|
|
|
+ let pages = self.pages2.lock().unwrap().clone();
|
|
|
+ let mut current_height = descent;
|
|
|
for page in pages {
|
|
|
- let page_off_y = descent + baseline + current_idx as f32 * line_height;
|
|
|
- if page_off_y > clip.h {
|
|
|
+ if current_height > rect.h {
|
|
|
break
|
|
|
}
|
|
|
|
|
|
- let mut mesh = MeshBuilder::new();
|
|
|
-
|
|
|
- for msg in page.msgs {
|
|
|
- let glyphs = msg.glyphs;
|
|
|
-
|
|
|
- let nick_color = self.select_nick_color(&msg.chatmsg.nick, &nick_colors);
|
|
|
-
|
|
|
- // Keep track of the 'section'
|
|
|
- // Section 0 is the timestamp
|
|
|
- // Section 1 is the nickname (colorized)
|
|
|
- // Finally is just the message itself
|
|
|
- let mut section = 2;
|
|
|
-
|
|
|
- let mut lines = text2::wrap(clip.w, font_size, &glyphs);
|
|
|
- // We are drawing bottom up but line wrap gives us lines in normal order
|
|
|
- lines.reverse();
|
|
|
- let last_idx = lines.len() - 1;
|
|
|
- for (i, line) in lines.into_iter().enumerate() {
|
|
|
- let off_y = descent + baseline + current_idx as f32 * line_height;
|
|
|
-
|
|
|
- if i == last_idx {
|
|
|
- section = 0;
|
|
|
- }
|
|
|
-
|
|
|
- // Render line
|
|
|
- let mut glyph_pos_iter = GlyphPositionIter::new(font_size, &line, baseline);
|
|
|
- for (mut glyph_rect, glyph) in glyph_pos_iter.zip(line.iter()) {
|
|
|
- let uv_rect =
|
|
|
- page.atlas.fetch_uv(glyph.glyph_id).expect("missing glyph UV rect");
|
|
|
- glyph_rect.y -= off_y;
|
|
|
-
|
|
|
- let color = match section {
|
|
|
- 0 => timestamp_color,
|
|
|
- 1 => nick_color,
|
|
|
- _ => text_color,
|
|
|
- };
|
|
|
-
|
|
|
- mesh.draw_box(&glyph_rect, color, uv_rect);
|
|
|
-
|
|
|
- if section < 2 && is_whitespace(&glyph.substr) {
|
|
|
- section += 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- current_idx += 1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let mesh = mesh.alloc(&self.render_api).await.unwrap();
|
|
|
-
|
|
|
- draws.push(DrawMesh {
|
|
|
- vertex_buffer: mesh.vertex_buffer,
|
|
|
- index_buffer: mesh.index_buffer,
|
|
|
- texture: Some(page.atlas.texture_id),
|
|
|
- num_elements: mesh.num_elements,
|
|
|
- });
|
|
|
+ let (mesh_inf, old_drawmesh) = page
|
|
|
+ .regen_mesh(
|
|
|
+ &rect,
|
|
|
+ &self.render_api,
|
|
|
+ font_size,
|
|
|
+ line_height,
|
|
|
+ baseline,
|
|
|
+ &nick_colors,
|
|
|
+ timest_color.clone(),
|
|
|
+ text_color.clone(),
|
|
|
+ )
|
|
|
+ .await;
|
|
|
+
|
|
|
+ // Apply scroll and scissor
|
|
|
+ // We use the scissor for scrolling
|
|
|
+ // Because we use the scissor, our actual rect is now rect instead of parent_rect
|
|
|
+ let off_x = 0.;
|
|
|
+ // This calc decides whether scroll is in terms of pages or pixels
|
|
|
+ let off_y = (self.scroll.get() - current_height + rect.h) / rect.h;
|
|
|
+ let scale_x = 1. / rect.w;
|
|
|
+ let scale_y = 1. / rect.h;
|
|
|
+ let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
|
|
|
+ glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
|
|
|
+
|
|
|
+ instrs.push(DrawInstruction::ApplyMatrix(model));
|
|
|
+
|
|
|
+ instrs.push(DrawInstruction::Draw(mesh_inf.mesh));
|
|
|
+
|
|
|
+ current_height += mesh_inf.px_height;
|
|
|
}
|
|
|
|
|
|
+ (instrs, old_drawmesh)
|
|
|
+
|
|
|
+ /*
|
|
|
if DEBUG_RENDER {
|
|
|
let mut debug_mesh = MeshBuilder::new();
|
|
|
debug_mesh.draw_outline(
|
|
|
@@ -569,6 +698,7 @@ impl ChatView {
|
|
|
}
|
|
|
|
|
|
draws
|
|
|
+ */
|
|
|
}
|
|
|
|
|
|
fn read_nick_colors(&self) -> Vec<Color> {
|
|
|
@@ -606,43 +736,24 @@ impl ChatView {
|
|
|
panic!("Node {:?} bad rect property", node);
|
|
|
};
|
|
|
|
|
|
- // TODO: Do we need this? Because of the viewport clipping
|
|
|
- rect.x += parent_rect.x;
|
|
|
- rect.y += parent_rect.y;
|
|
|
-
|
|
|
- let drawcalls = self.regen_mesh(rect.clone()).await;
|
|
|
- let old_drawcalls =
|
|
|
- std::mem::replace(&mut *self.drawcalls.lock().unwrap(), drawcalls.clone());
|
|
|
- let mut drawcalls: Vec<_> =
|
|
|
- drawcalls.into_iter().map(|dc| DrawInstruction::Draw(dc)).collect();
|
|
|
+ let timer = std::time::Instant::now();
|
|
|
+ let (mut mesh_instrs, mut old_drawmesh) = self.regen_mesh(rect.clone()).await;
|
|
|
+ debug!(target: "ui::chatview", "regen_mesh() took {:?}", timer.elapsed());
|
|
|
|
|
|
let mut freed_textures = vec![];
|
|
|
let mut freed_buffers = vec![];
|
|
|
- for old_dc in old_drawcalls {
|
|
|
- freed_buffers.push(old_dc.vertex_buffer);
|
|
|
- freed_buffers.push(old_dc.index_buffer);
|
|
|
- if let Some(texture_id) = old_dc.texture {
|
|
|
- freed_textures.push(texture_id);
|
|
|
- }
|
|
|
+ for old_mesh in old_drawmesh {
|
|
|
+ freed_buffers.push(old_mesh.vertex_buffer);
|
|
|
+ freed_buffers.push(old_mesh.index_buffer);
|
|
|
+ //if let Some(texture_id) = old_dc.texture {
|
|
|
+ // freed_textures.push(texture_id);
|
|
|
+ //}
|
|
|
}
|
|
|
|
|
|
debug!(target: "ui::chatview", "chatview rect = {:?}", rect);
|
|
|
|
|
|
- // Apply scroll and scissor
|
|
|
- // We use the scissor for scrolling
|
|
|
- // Because we use the scissor, our actual rect is now rect instead of parent_rect
|
|
|
- let off_x = 0.;
|
|
|
- // This calc decides whether scroll is in terms of pages or pixels
|
|
|
- let off_y = (self.scroll.get() + rect.h) / rect.h;
|
|
|
- let scale_x = 1. / rect.w;
|
|
|
- let scale_y = 1. / rect.h;
|
|
|
- let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
|
|
|
- glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
|
|
|
-
|
|
|
- let mut instrs =
|
|
|
- vec![DrawInstruction::ApplyViewport(rect), DrawInstruction::ApplyMatrix(model)];
|
|
|
- //let mut instrs = vec![DrawInstruction::ApplyMatrix(model)];
|
|
|
- instrs.append(&mut drawcalls);
|
|
|
+ let mut instrs = vec![DrawInstruction::ApplyViewport(rect)];
|
|
|
+ instrs.append(&mut mesh_instrs);
|
|
|
|
|
|
Some(DrawUpdate {
|
|
|
key: self.dc_key,
|