text_renderer: clear VkImage before growing, implement island padding in texture atlas (fix graphical artifacts)

This commit is contained in:
Aleksander
2025-11-08 12:08:48 +01:00
parent 6f41ffe59c
commit a59a4c2870
3 changed files with 51 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
use cosmic_text::{FontSystem, SwashCache};
use etagere::{size2, Allocation, BucketedAtlasAllocator};
use etagere::{Allocation, BucketedAtlasAllocator, size2};
use lru::LruCache;
use rustc_hash::FxHasher;
use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc};
@@ -8,20 +8,20 @@ use vulkano::{
command_buffer::CommandBufferUsage,
descriptor_set::DescriptorSet,
format::Format,
image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
memory::allocator::AllocationCreateInfo,
pipeline::graphics::vertex_input::Vertex,
};
use super::{
GlyphDetails, GpuCacheStatus,
custom_glyph::ContentType,
shaders::{frag_atlas, vert_atlas},
text_renderer::GlyphonCacheKey,
GlyphDetails, GpuCacheStatus,
};
use crate::gfx::{
BLEND_ALPHA, WGfx,
pipeline::{WGfxPipeline, WPipelineCreateInfo},
WGfx, BLEND_ALPHA,
};
/// Pipeline & shaders to be reused between `TextRenderer` instances
@@ -77,6 +77,8 @@ pub(super) struct InnerAtlas {
common: TextPipeline,
}
pub const TEXT_ATLAS_ISLAND_PADDING_PX: u32 = 1;
impl InnerAtlas {
const INITIAL_SIZE: u32 = 256;
@@ -131,10 +133,15 @@ impl InnerAtlas {
}
pub(super) fn try_allocate(&mut self, width: usize, height: usize) -> Option<Allocation> {
let size = size2(width as i32, height as i32);
let glyph_size = size2(width as i32, height as i32);
let padded_size = glyph_size
+ size2(
(TEXT_ATLAS_ISLAND_PADDING_PX * 2) as i32,
(TEXT_ATLAS_ISLAND_PADDING_PX * 2) as i32,
);
loop {
let allocation = self.packer.allocate(size);
let allocation = self.packer.allocate(padded_size);
if allocation.is_some() {
return allocation;
@@ -200,6 +207,9 @@ impl InnerAtlas {
.gfx
.create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
// clear newly allocated image with zeros
cmd_buf.clear_image(&image)?;
// Re-upload glyphs
for (&cache_key, glyph) in &self.glyph_cache {
let (x, y) = match glyph.gpu_cache {

View File

@@ -1,14 +1,15 @@
use crate::{
gfx::{cmd::GfxCommandBuffer, pass::WGfxPass},
renderer_vk::{model_buffer::ModelBuffer, viewport::Viewport},
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 glam::{Mat4, Vec2, Vec3};
use vulkano::{
buffer::{BufferUsage, Subbuffer},
@@ -359,14 +360,29 @@ fn prepare_glyph(
inner = par.atlas.inner_for_content_mut(image.content_type);
};
let atlas_min = allocation.rectangle.min;
let atlas_with_island_min = allocation.rectangle.min;
let size_with_island = allocation.rectangle.size();
let atlas_glyph_min =
allocation.rectangle.min + size2(TEXT_ATLAS_ISLAND_PADDING_PX as i32, TEXT_ATLAS_ISLAND_PADDING_PX as i32);
let mut cmd_buf = gfx.create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
// Set data to zeros for the whole glyph island
// TODO: use `vkCmdClearColorImage` with an image subresource (or xywh region?) to omit unnecessary allocation
let zero_bytes_data: Vec<u8> = vec![0x00; size_with_island.area() as usize * 4 /* RGBX */];
cmd_buf.update_image(
inner.image_view.image(),
&zero_bytes_data,
[atlas_with_island_min.x as u32, atlas_with_island_min.y as u32, 0],
Some([size_with_island.width as u32, size_with_island.height as u32, 1]),
)?;
// Upload glyph itself
cmd_buf.update_image(
inner.image_view.image(),
&image.data,
[atlas_min.x as _, atlas_min.y as _, 0],
[atlas_glyph_min.x as u32, atlas_glyph_min.y as u32, 0],
Some([image.width.into(), image.height.into(), 1]),
)?;
@@ -374,8 +390,8 @@ fn prepare_glyph(
(
GpuCacheStatus::InAtlas {
x: atlas_min.x as u16,
y: atlas_min.y as u16,
x: atlas_glyph_min.x as u16,
y: atlas_glyph_min.y as u16,
content_type: image.content_type,
},
Some(allocation.id),