text_renderer: clear VkImage before growing, implement island padding in texture atlas (fix graphical artifacts)
This commit is contained in:
@@ -4,11 +4,11 @@ use vulkano::{
|
||||
DeviceSize,
|
||||
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
||||
command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferExecFuture, CopyBufferToImageInfo, CopyImageInfo, PrimaryAutoCommandBuffer,
|
||||
PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, SubpassContents,
|
||||
AutoCommandBufferBuilder, ClearColorImageInfo, CommandBufferExecFuture, CopyBufferToImageInfo, CopyImageInfo,
|
||||
PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, SubpassContents,
|
||||
},
|
||||
device::Queue,
|
||||
format::{ClearValue, Format},
|
||||
format::{ClearColorValue, ClearValue, Format},
|
||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
|
||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
||||
@@ -128,6 +128,16 @@ impl WCommandBuffer<CmdBufXfer> {
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
pub fn clear_image(&mut self, image: &Arc<Image>) -> anyhow::Result<()> {
|
||||
let clear_info = ClearColorImageInfo {
|
||||
clear_value: ClearColorValue::Uint([0, 0, 0, 0]),
|
||||
..ClearColorImageInfo::image(image.clone())
|
||||
};
|
||||
|
||||
self.command_buffer.clear_color_image(clear_info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_image(
|
||||
&mut self,
|
||||
image: &Arc<Image>,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user