remove unused pass cache, cache imageviews instead

This commit is contained in:
galister 2026-05-29 11:04:55 +09:00
parent 1591466a8d
commit 57765485bb
6 changed files with 79 additions and 105 deletions

View File

@ -289,6 +289,7 @@ pub struct Rectangle {
pub struct ImagePrimitive { pub struct ImagePrimitive {
pub content: CustomGlyphData, pub content: CustomGlyphData,
pub content_key: usize, pub content_key: usize,
pub skip_cache: bool,
pub border: f32, // width in pixels pub border: f32, // width in pixels
pub border_color: Color, pub border_color: Color,

View File

@ -2,22 +2,22 @@ use std::{cell::RefCell, rc::Rc, sync::Arc};
use cosmic_text::Buffer; use cosmic_text::Buffer;
use glam::{Mat4, Vec2, Vec3}; use glam::{Mat4, Vec2, Vec3};
use slotmap::{SlotMap, new_key_type}; use slotmap::{new_key_type, SlotMap};
use vulkano::pipeline::graphics::viewport; use vulkano::pipeline::graphics::viewport;
use crate::{ use crate::{
drawing::{self}, drawing::{self},
font_config, font_config,
gfx::{WGfx, cmd::GfxCommandBuffer}, gfx::{cmd::GfxCommandBuffer, WGfx},
renderer_vk::image::{ImagePipeline, ImageRenderer}, renderer_vk::image::{ImagePipeline, ImageRenderer, ImageViewCache},
}; };
use super::{ use super::{
rect::{RectPipeline, RectRenderer}, rect::{RectPipeline, RectRenderer},
text::{ text::{
DEFAULT_METRICS, SWASH_CACHE, TextArea, TextBounds,
text_atlas::{TextAtlas, TextPipeline}, text_atlas::{TextAtlas, TextPipeline},
text_renderer::TextRenderer, text_renderer::TextRenderer,
TextArea, TextBounds, DEFAULT_METRICS, SWASH_CACHE,
}, },
viewport::Viewport, viewport::Viewport,
}; };
@ -62,6 +62,7 @@ impl RendererPass<'_> {
viewport: &mut Viewport, viewport: &mut Viewport,
cmd_buf: &mut GfxCommandBuffer, cmd_buf: &mut GfxCommandBuffer,
text_atlas: &mut TextAtlas, text_atlas: &mut TextAtlas,
image_view_cache: &mut ImageViewCache,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if self.submitted { if self.submitted {
return Ok(()); return Ok(());
@ -95,7 +96,9 @@ impl RendererPass<'_> {
self.submitted = true; self.submitted = true;
self.rect_renderer.render(gfx, viewport, &vk_scissor, cmd_buf)?; 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(); let mut font_system = font_system.system.lock();
@ -169,6 +172,7 @@ pub struct Context {
pub dirty: bool, pub dirty: bool,
pixel_scale: f32, pixel_scale: f32,
empty_text: Rc<RefCell<Buffer>>, empty_text: Rc<RefCell<Buffer>>,
image_cache: ImageViewCache,
} }
pub struct ContextDrawResult { pub struct ContextDrawResult {
@ -187,6 +191,7 @@ impl Context {
pixel_scale, pixel_scale,
dirty: true, dirty: true,
empty_text: Rc::new(RefCell::new(Buffer::new_empty(DEFAULT_METRICS))), empty_text: Rc::new(RefCell::new(Buffer::new_empty(DEFAULT_METRICS))),
image_cache: ImageViewCache::new(),
}) })
} }
@ -339,6 +344,7 @@ impl Context {
&mut self.viewport, &mut self.viewport,
cmd_buf, cmd_buf,
&mut atlas.text_atlas, &mut atlas.text_atlas,
&mut self.image_cache,
)?; )?;
} }

View File

@ -4,7 +4,7 @@ use cosmic_text::SubpixelBin;
use glam::Mat4; use glam::Mat4;
use smallvec::smallvec; use smallvec::smallvec;
use vulkano::{ use vulkano::{
buffer::{BufferContents, BufferUsage, Subbuffer}, buffer::{BufferContents, BufferUsage},
command_buffer::CommandBufferUsage, command_buffer::CommandBufferUsage,
format::Format, format::Format,
image::view::ImageView, image::view::ImageView,
@ -14,10 +14,9 @@ use vulkano::{
use crate::{ use crate::{
drawing::{Boundary, ImagePrimitive}, drawing::{Boundary, ImagePrimitive},
gfx::{ gfx::{
BLEND_ALPHA, WGfx,
cmd::GfxCommandBuffer, cmd::GfxCommandBuffer,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo}, pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx, BLEND_ALPHA,
}, },
renderer_vk::{ renderer_vk::{
model_buffer::ModelBuffer, model_buffer::ModelBuffer,
@ -65,24 +64,24 @@ impl ImagePipeline {
} }
} }
pub type ImageViewCache = HashMap<usize, CachedImageView>;
pub struct CachedImageView {
view: Arc<ImageView>,
res: [u32; 2],
}
struct ImageVertexWithContent { struct ImageVertexWithContent {
vert: ImageVertex, vert: ImageVertex,
content: CustomGlyphData, content: CustomGlyphData,
content_key: usize, // identifies an image tag. content_key: usize, // identifies an image tag.
} skip_cache: bool,
struct CachedPass {
content_id: usize,
vert_buffer: Subbuffer<[ImageVertex]>,
inner: WGfxPass<ImageVertex>,
res: [u32; 2],
} }
pub struct ImageRenderer { pub struct ImageRenderer {
pipeline: ImagePipeline, pipeline: ImagePipeline,
image_verts: Vec<ImageVertexWithContent>, image_verts: Vec<ImageVertexWithContent>,
model_buffer: ModelBuffer, model_buffer: ModelBuffer,
cached_passes: HashMap<usize, CachedPass>,
} }
impl ImageRenderer { impl ImageRenderer {
@ -91,15 +90,9 @@ impl ImageRenderer {
model_buffer: ModelBuffer::new(&pipeline.gfx)?, model_buffer: ModelBuffer::new(&pipeline.gfx)?,
pipeline, pipeline,
image_verts: vec![], 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) { pub fn add_image(&mut self, boundary: Boundary, image: ImagePrimitive, transform: &Mat4) {
let in_model_idx = self let in_model_idx = self
.model_buffer .model_buffer
@ -119,6 +112,7 @@ impl ImageRenderer {
}, },
content: image.content, content: image.content,
content_key: image.content_key, content_key: image.content_key,
skip_cache: image.skip_cache,
}); });
} }
@ -159,65 +153,58 @@ impl ImageRenderer {
viewport: &mut Viewport, viewport: &mut Viewport,
vk_scissor: &graphics::viewport::Scissor, vk_scissor: &graphics::viewport::Scissor,
cmd_buf: &mut GfxCommandBuffer, cmd_buf: &mut GfxCommandBuffer,
image_view_cache: &mut ImageViewCache,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let res = viewport.resolution(); let res = viewport.resolution();
self.model_buffer.upload(gfx)?; self.model_buffer.upload(gfx)?;
for img in &self.image_verts { for img in &self.image_verts {
let pass = if let Some(x) = self.cached_passes.get_mut(&img.content_key) { let image_view = if let Some(x) = image_view_cache.get_mut(&img.content_key) {
if x.content_id != img.content.id || x.res != res { if img.skip_cache || x.res != res {
// image changed // image changed
let Some(image_view) = Self::upload_image(&self.pipeline.gfx, res, img)? else { let Some(image_view) = Self::upload_image(&self.pipeline.gfx, res, img)? else {
continue; continue;
}; };
x.inner x.view = image_view;
.update_sampler(2, image_view, self.pipeline.gfx.texture_filter)?; x.res = res;
} }
x x.view.clone()
} else { } else {
let vert_buffer = self.pipeline.gfx.empty_buffer(
BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST,
(std::mem::size_of::<ImageVertex>()) as _,
)?;
let Some(image_view) = Self::upload_image(&self.pipeline.gfx, res, img)? else { let Some(image_view) = Self::upload_image(&self.pipeline.gfx, res, img)? else {
continue; continue;
}; };
let set0 = viewport.get_image_descriptor(&self.pipeline); image_view_cache.insert(img.content_key, CachedImageView { view: image_view, res });
let set1 = self.model_buffer.get_image_descriptor(&self.pipeline); image_view_cache.get_mut(&img.content_key).unwrap().view.clone()
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()
}; };
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::<ImageVertex>()) 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(()) Ok(())

View File

@ -10,10 +10,9 @@ use vulkano::{
use crate::{ use crate::{
drawing::{Boundary, Rectangle}, drawing::{Boundary, Rectangle},
gfx::{ gfx::{
BLEND_ALPHA, WGfx,
cmd::GfxCommandBuffer, cmd::GfxCommandBuffer,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo}, pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx, BLEND_ALPHA,
}, },
renderer_vk::model_buffer::ModelBuffer, renderer_vk::model_buffer::ModelBuffer,
}; };
@ -59,18 +58,12 @@ impl RectPipeline {
} }
} }
struct CachedPass {
pass: WGfxPass<RectVertex>,
res: [u32; 2],
}
pub struct RectRenderer { pub struct RectRenderer {
pipeline: RectPipeline, pipeline: RectPipeline,
rect_vertices: Vec<RectVertex>, rect_vertices: Vec<RectVertex>,
vert_buffer: Subbuffer<[RectVertex]>, vert_buffer: Subbuffer<[RectVertex]>,
vert_buffer_len: usize, vert_buffer_len: usize,
model_buffer: ModelBuffer, model_buffer: ModelBuffer,
pass: Option<CachedPass>,
} }
impl RectRenderer { impl RectRenderer {
@ -88,15 +81,9 @@ impl RectRenderer {
rect_vertices: vec![], rect_vertices: vec![],
vert_buffer, vert_buffer,
vert_buffer_len: BUFFER_SIZE, 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) { pub fn add_rect(&mut self, boundary: Boundary, rectangle: Rectangle, transform: &Mat4) {
let in_model_idx = self let in_model_idx = self
.model_buffer .model_buffer
@ -144,27 +131,20 @@ impl RectRenderer {
self.model_buffer.upload(gfx)?; self.model_buffer.upload(gfx)?;
self.upload_verts()?; self.upload_verts()?;
let cache = match self.pass.take() { let set0 = viewport.get_rect_descriptor(&self.pipeline);
Some(p) if p.res == res => p, let set1 = self.model_buffer.get_rect_descriptor(&self.pipeline);
_ => { let pass = self.pipeline.color_rect.create_pass(
let set0 = viewport.get_rect_descriptor(&self.pipeline); [res[0] as _, res[1] as _],
let set1 = self.model_buffer.get_rect_descriptor(&self.pipeline); [0.0, 0.0],
let pass = self.pipeline.color_rect.create_pass( self.vert_buffer.clone(),
[res[0] as _, res[1] as _], 0..4,
[0.0, 0.0], 0..self.rect_vertices.len() as _,
self.vert_buffer.clone(), vec![set0, set1],
0..4, vk_scissor,
0..self.rect_vertices.len() as _, )?;
vec![set0, set1],
vk_scissor,
)?;
CachedPass { pass, res }
}
};
self.rect_vertices.clear(); self.rect_vertices.clear();
cmd_buf.run_ref(&cache.pass)?; cmd_buf.run_ref(&pass)?;
self.pass = Some(cache);
Ok(()) Ok(())
} }
} }

View File

@ -1,27 +1,24 @@
use crate::{ 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}, renderer_vk::{model_buffer::ModelBuffer, text::text_atlas::TEXT_ATLAS_ISLAND_PADDING_PX, viewport::Viewport},
}; };
use super::{ use super::{
ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea,
custom_glyph::{CustomGlyphCacheKey, RasterizeCustomGlyphRequest, RasterizedCustomGlyph}, custom_glyph::{CustomGlyphCacheKey, RasterizeCustomGlyphRequest, RasterizedCustomGlyph},
text_atlas::{GlyphVertex, TextAtlas, TextPipeline}, text_atlas::{GlyphVertex, TextAtlas, TextPipeline},
ContentType, FontSystem, GlyphDetails, GpuCacheStatus, SwashCache, TextArea,
}; };
use cosmic_text::{Color, SubpixelBin, SwashContent}; use cosmic_text::{Color, SubpixelBin, SwashContent};
use etagere::size2; use etagere::{size2, AllocId};
use glam::{Mat4, Vec2, Vec3}; use glam::{Mat4, Vec2, Vec3};
use std::collections::HashSet;
use vulkano::{ use vulkano::{
buffer::{BufferUsage, Subbuffer}, buffer::{BufferUsage, Subbuffer},
command_buffer::CommandBufferUsage, command_buffer::CommandBufferUsage,
pipeline::graphics, pipeline::graphics,
}; };
struct CachedPass {
pass: WGfxPass<GlyphVertex>,
res: [u32; 2],
}
/// A text renderer that uses cached glyphs to render text into an existing render pass. /// A text renderer that uses cached glyphs to render text into an existing render pass.
pub struct TextRenderer { pub struct TextRenderer {
pipeline: TextPipeline, pipeline: TextPipeline,
@ -29,7 +26,6 @@ pub struct TextRenderer {
vertex_buffer_capacity: usize, vertex_buffer_capacity: usize,
glyph_vertices: Vec<GlyphVertex>, glyph_vertices: Vec<GlyphVertex>,
model_buffer: ModelBuffer, model_buffer: ModelBuffer,
pass: Option<CachedPass>,
} }
impl TextRenderer { impl TextRenderer {
@ -49,7 +45,6 @@ impl TextRenderer {
vertex_buffer, vertex_buffer,
vertex_buffer_capacity: INITIAL_CAPACITY, vertex_buffer_capacity: INITIAL_CAPACITY,
glyph_vertices: Vec::new(), glyph_vertices: Vec::new(),
pass: None,
}) })
} }

View File

@ -8,7 +8,7 @@ use crate::{
globals::Globals, globals::Globals,
layout::WidgetID, layout::WidgetID,
renderer_vk::text::custom_glyph::CustomGlyphData, renderer_vk::text::custom_glyph::CustomGlyphData,
widget::{WidgetStateFlags, util::WLength}, widget::{util::WLength, WidgetStateFlags},
}; };
use super::{WidgetObj, WidgetState}; use super::{WidgetObj, WidgetState};
@ -30,6 +30,7 @@ pub struct WidgetImage {
params: WidgetImageParams, params: WidgetImageParams,
id: WidgetID, id: WidgetID,
content_key: usize, content_key: usize,
dirty: bool,
} }
impl WidgetImage { impl WidgetImage {
@ -40,6 +41,7 @@ impl WidgetImage {
params, params,
id: WidgetID::null(), id: WidgetID::null(),
content_key: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed), content_key: AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed),
dirty: true,
}), }),
) )
} }
@ -50,6 +52,7 @@ impl WidgetImage {
} }
self.params.glyph_data = content; self.params.glyph_data = content;
self.dirty = true;
alterables.mark_dirty_and_redraw(self.id); alterables.mark_dirty_and_redraw(self.id);
} }
@ -79,11 +82,13 @@ impl WidgetObj for WidgetImage {
ImagePrimitive { ImagePrimitive {
content, content,
content_key: self.content_key, content_key: self.content_key,
skip_cache: self.dirty,
border: self.params.border, border: self.params.border,
border_color: self.params.border_color, border_color: self.params.border_color,
round_units, round_units,
}, },
)); ));
self.dirty = false;
} }
fn measure( fn measure(