diff --git a/wgui/src/drawing.rs b/wgui/src/drawing.rs index 7acc52e9..c0c59ea1 100644 --- a/wgui/src/drawing.rs +++ b/wgui/src/drawing.rs @@ -289,6 +289,7 @@ pub struct Rectangle { pub struct ImagePrimitive { pub content: CustomGlyphData, pub content_key: usize, + pub skip_cache: bool, pub border: f32, // width in pixels pub border_color: Color, diff --git a/wgui/src/renderer_vk/context.rs b/wgui/src/renderer_vk/context.rs index 4dd552dc..da329b45 100644 --- a/wgui/src/renderer_vk/context.rs +++ b/wgui/src/renderer_vk/context.rs @@ -2,22 +2,22 @@ use std::{cell::RefCell, rc::Rc, sync::Arc}; use cosmic_text::Buffer; use glam::{Mat4, Vec2, Vec3}; -use slotmap::{SlotMap, new_key_type}; +use slotmap::{new_key_type, SlotMap}; use vulkano::pipeline::graphics::viewport; use crate::{ drawing::{self}, font_config, - gfx::{WGfx, cmd::GfxCommandBuffer}, - renderer_vk::image::{ImagePipeline, ImageRenderer}, + gfx::{cmd::GfxCommandBuffer, WGfx}, + renderer_vk::image::{ImagePipeline, ImageRenderer, ImageViewCache}, }; use super::{ rect::{RectPipeline, RectRenderer}, text::{ - DEFAULT_METRICS, SWASH_CACHE, TextArea, TextBounds, text_atlas::{TextAtlas, TextPipeline}, text_renderer::TextRenderer, + TextArea, TextBounds, DEFAULT_METRICS, SWASH_CACHE, }, viewport::Viewport, }; @@ -62,6 +62,7 @@ impl RendererPass<'_> { viewport: &mut Viewport, cmd_buf: &mut GfxCommandBuffer, text_atlas: &mut TextAtlas, + image_view_cache: &mut ImageViewCache, ) -> anyhow::Result<()> { if self.submitted { return Ok(()); @@ -95,7 +96,9 @@ impl RendererPass<'_> { self.submitted = true; self.rect_renderer.render(gfx, viewport, &vk_scissor, cmd_buf)?; - self.image_renderer.render(gfx, viewport, &vk_scissor, cmd_buf)?; + self + .image_renderer + .render(gfx, viewport, &vk_scissor, cmd_buf, image_view_cache)?; { let mut font_system = font_system.system.lock(); @@ -169,6 +172,7 @@ pub struct Context { pub dirty: bool, pixel_scale: f32, empty_text: Rc>, + image_cache: ImageViewCache, } pub struct ContextDrawResult { @@ -187,6 +191,7 @@ impl Context { pixel_scale, dirty: true, empty_text: Rc::new(RefCell::new(Buffer::new_empty(DEFAULT_METRICS))), + image_cache: ImageViewCache::new(), }) } @@ -339,6 +344,7 @@ impl Context { &mut self.viewport, cmd_buf, &mut atlas.text_atlas, + &mut self.image_cache, )?; } diff --git a/wgui/src/renderer_vk/image.rs b/wgui/src/renderer_vk/image.rs index bf2aacca..c412d3f5 100644 --- a/wgui/src/renderer_vk/image.rs +++ b/wgui/src/renderer_vk/image.rs @@ -4,7 +4,7 @@ use cosmic_text::SubpixelBin; use glam::Mat4; use smallvec::smallvec; use vulkano::{ - buffer::{BufferContents, BufferUsage, Subbuffer}, + buffer::{BufferContents, BufferUsage}, command_buffer::CommandBufferUsage, format::Format, image::view::ImageView, @@ -14,10 +14,9 @@ use vulkano::{ use crate::{ drawing::{Boundary, ImagePrimitive}, gfx::{ - BLEND_ALPHA, WGfx, cmd::GfxCommandBuffer, - pass::WGfxPass, pipeline::{WGfxPipeline, WPipelineCreateInfo}, + WGfx, BLEND_ALPHA, }, renderer_vk::{ model_buffer::ModelBuffer, @@ -65,24 +64,24 @@ impl ImagePipeline { } } +pub type ImageViewCache = HashMap; + +pub struct CachedImageView { + view: Arc, + res: [u32; 2], +} + struct ImageVertexWithContent { vert: ImageVertex, content: CustomGlyphData, content_key: usize, // identifies an image tag. -} - -struct CachedPass { - content_id: usize, - vert_buffer: Subbuffer<[ImageVertex]>, - inner: WGfxPass, - res: [u32; 2], + skip_cache: bool, } pub struct ImageRenderer { pipeline: ImagePipeline, image_verts: Vec, model_buffer: ModelBuffer, - cached_passes: HashMap, } impl ImageRenderer { @@ -91,15 +90,9 @@ impl ImageRenderer { model_buffer: ModelBuffer::new(&pipeline.gfx)?, pipeline, image_verts: vec![], - cached_passes: HashMap::new(), }) } - pub fn begin(&mut self) { - self.image_verts.clear(); - self.model_buffer.begin(); - } - pub fn add_image(&mut self, boundary: Boundary, image: ImagePrimitive, transform: &Mat4) { let in_model_idx = self .model_buffer @@ -119,6 +112,7 @@ impl ImageRenderer { }, content: image.content, content_key: image.content_key, + skip_cache: image.skip_cache, }); } @@ -159,65 +153,58 @@ impl ImageRenderer { viewport: &mut Viewport, vk_scissor: &graphics::viewport::Scissor, cmd_buf: &mut GfxCommandBuffer, + image_view_cache: &mut ImageViewCache, ) -> anyhow::Result<()> { let res = viewport.resolution(); self.model_buffer.upload(gfx)?; for img in &self.image_verts { - let pass = if let Some(x) = self.cached_passes.get_mut(&img.content_key) { - if x.content_id != img.content.id || x.res != res { + let image_view = if let Some(x) = image_view_cache.get_mut(&img.content_key) { + if img.skip_cache || x.res != res { // image changed let Some(image_view) = Self::upload_image(&self.pipeline.gfx, res, img)? else { continue; }; - x.inner - .update_sampler(2, image_view, self.pipeline.gfx.texture_filter)?; + x.view = image_view; + x.res = res; } - x + x.view.clone() } else { - let vert_buffer = self.pipeline.gfx.empty_buffer( - BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST, - (std::mem::size_of::()) as _, - )?; - let Some(image_view) = Self::upload_image(&self.pipeline.gfx, res, img)? else { continue; }; - let set0 = viewport.get_image_descriptor(&self.pipeline); - let set1 = self.model_buffer.get_image_descriptor(&self.pipeline); - let set2 = self - .pipeline - .inner - .uniform_sampler(2, image_view, self.pipeline.gfx.texture_filter)?; - - let pass = self.pipeline.inner.create_pass( - [res[0] as _, res[1] as _], - [0.0, 0.0], - vert_buffer.clone(), - 0..4, - 0..1, - vec![set0, set1, set2], - vk_scissor, - )?; - - self.cached_passes.insert( - img.content_key, - CachedPass { - content_id: img.content.id, - vert_buffer, - inner: pass, - res, - }, - ); - self.cached_passes.get_mut(&img.content_key).unwrap() + image_view_cache.insert(img.content_key, CachedImageView { view: image_view, res }); + image_view_cache.get_mut(&img.content_key).unwrap().view.clone() }; - pass.vert_buffer.write()?[0..1].clone_from_slice(&[img.vert]); + let vert_buffer = self.pipeline.gfx.empty_buffer( + BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST, + (std::mem::size_of::()) as _, + )?; - cmd_buf.run_ref(&pass.inner)?; + let set0 = viewport.get_image_descriptor(&self.pipeline); + let set1 = self.model_buffer.get_image_descriptor(&self.pipeline); + let set2 = self + .pipeline + .inner + .uniform_sampler(2, image_view, self.pipeline.gfx.texture_filter)?; + + let pass = self.pipeline.inner.create_pass( + [res[0] as _, res[1] as _], + [0.0, 0.0], + vert_buffer.clone(), + 0..4, + 0..1, + vec![set0, set1, set2], + vk_scissor, + )?; + + vert_buffer.write()?[0..1].clone_from_slice(&[img.vert]); + + cmd_buf.run_ref(&pass)?; } Ok(()) diff --git a/wgui/src/renderer_vk/rect.rs b/wgui/src/renderer_vk/rect.rs index 4047d65d..f0b294f5 100644 --- a/wgui/src/renderer_vk/rect.rs +++ b/wgui/src/renderer_vk/rect.rs @@ -10,10 +10,9 @@ use vulkano::{ use crate::{ drawing::{Boundary, Rectangle}, gfx::{ - BLEND_ALPHA, WGfx, cmd::GfxCommandBuffer, - pass::WGfxPass, pipeline::{WGfxPipeline, WPipelineCreateInfo}, + WGfx, BLEND_ALPHA, }, renderer_vk::model_buffer::ModelBuffer, }; @@ -59,18 +58,12 @@ impl RectPipeline { } } -struct CachedPass { - pass: WGfxPass, - res: [u32; 2], -} - pub struct RectRenderer { pipeline: RectPipeline, rect_vertices: Vec, vert_buffer: Subbuffer<[RectVertex]>, vert_buffer_len: usize, model_buffer: ModelBuffer, - pass: Option, } impl RectRenderer { @@ -88,15 +81,9 @@ impl RectRenderer { rect_vertices: vec![], vert_buffer, vert_buffer_len: BUFFER_SIZE, - pass: None, }) } - pub fn begin(&mut self) { - self.rect_vertices.clear(); - self.model_buffer.begin(); - } - pub fn add_rect(&mut self, boundary: Boundary, rectangle: Rectangle, transform: &Mat4) { let in_model_idx = self .model_buffer @@ -144,27 +131,20 @@ impl RectRenderer { self.model_buffer.upload(gfx)?; self.upload_verts()?; - let cache = match self.pass.take() { - Some(p) if p.res == res => p, - _ => { - let set0 = viewport.get_rect_descriptor(&self.pipeline); - let set1 = self.model_buffer.get_rect_descriptor(&self.pipeline); - let pass = self.pipeline.color_rect.create_pass( - [res[0] as _, res[1] as _], - [0.0, 0.0], - self.vert_buffer.clone(), - 0..4, - 0..self.rect_vertices.len() as _, - vec![set0, set1], - vk_scissor, - )?; - CachedPass { pass, res } - } - }; + let set0 = viewport.get_rect_descriptor(&self.pipeline); + let set1 = self.model_buffer.get_rect_descriptor(&self.pipeline); + let pass = self.pipeline.color_rect.create_pass( + [res[0] as _, res[1] as _], + [0.0, 0.0], + self.vert_buffer.clone(), + 0..4, + 0..self.rect_vertices.len() as _, + vec![set0, set1], + vk_scissor, + )?; self.rect_vertices.clear(); - cmd_buf.run_ref(&cache.pass)?; - self.pass = Some(cache); + cmd_buf.run_ref(&pass)?; Ok(()) } } diff --git a/wgui/src/renderer_vk/text/text_renderer.rs b/wgui/src/renderer_vk/text/text_renderer.rs index a02d7e49..e933a1a6 100644 --- a/wgui/src/renderer_vk/text/text_renderer.rs +++ b/wgui/src/renderer_vk/text/text_renderer.rs @@ -1,27 +1,24 @@ use crate::{ - gfx::{cmd::GfxCommandBuffer, pass::WGfxPass}, + gfx::cmd::GfxCommandBuffer, renderer_vk::{model_buffer::ModelBuffer, text::text_atlas::TEXT_ATLAS_ISLAND_PADDING_PX, viewport::Viewport}, }; use super::{ - ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea, custom_glyph::{CustomGlyphCacheKey, RasterizeCustomGlyphRequest, RasterizedCustomGlyph}, text_atlas::{GlyphVertex, TextAtlas, TextPipeline}, + ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea, }; use cosmic_text::{Color, SubpixelBin, SwashContent}; -use etagere::size2; +use etagere::{size2, AllocId}; use glam::{Mat4, Vec2, Vec3}; +use std::collections::HashSet; + use vulkano::{ buffer::{BufferUsage, Subbuffer}, command_buffer::CommandBufferUsage, pipeline::graphics, }; -struct CachedPass { - pass: WGfxPass, - res: [u32; 2], -} - /// A text renderer that uses cached glyphs to render text into an existing render pass. pub struct TextRenderer { pipeline: TextPipeline, @@ -29,7 +26,6 @@ pub struct TextRenderer { vertex_buffer_capacity: usize, glyph_vertices: Vec, model_buffer: ModelBuffer, - pass: Option, } impl TextRenderer { @@ -49,7 +45,6 @@ impl TextRenderer { vertex_buffer, vertex_buffer_capacity: INITIAL_CAPACITY, glyph_vertices: Vec::new(), - pass: None, }) } diff --git a/wgui/src/widget/image.rs b/wgui/src/widget/image.rs index 0a63ebac..2237ba50 100644 --- a/wgui/src/widget/image.rs +++ b/wgui/src/widget/image.rs @@ -8,7 +8,7 @@ use crate::{ globals::Globals, layout::WidgetID, renderer_vk::text::custom_glyph::CustomGlyphData, - widget::{WidgetStateFlags, util::WLength}, + widget::{util::WLength, WidgetStateFlags}, }; use super::{WidgetObj, WidgetState}; @@ -30,6 +30,7 @@ pub struct WidgetImage { params: WidgetImageParams, id: WidgetID, content_key: usize, + dirty: bool, } impl WidgetImage { @@ -40,6 +41,7 @@ impl WidgetImage { params, id: WidgetID::null(), content_key: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed), + dirty: true, }), ) } @@ -50,6 +52,7 @@ impl WidgetImage { } self.params.glyph_data = content; + self.dirty = true; alterables.mark_dirty_and_redraw(self.id); } @@ -79,11 +82,13 @@ impl WidgetObj for WidgetImage { ImagePrimitive { content, content_key: self.content_key, + skip_cache: self.dirty, border: self.params.border, border_color: self.params.border_color, round_units, }, )); + self.dirty = false; } fn measure(