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,
|
DeviceSize,
|
||||||
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer},
|
||||||
command_buffer::{
|
command_buffer::{
|
||||||
AutoCommandBufferBuilder, CommandBufferExecFuture, CopyBufferToImageInfo, CopyImageInfo, PrimaryAutoCommandBuffer,
|
AutoCommandBufferBuilder, ClearColorImageInfo, CommandBufferExecFuture, CopyBufferToImageInfo, CopyImageInfo,
|
||||||
PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, SubpassContents,
|
PrimaryAutoCommandBuffer, PrimaryCommandBufferAbstract, RenderingAttachmentInfo, RenderingInfo, SubpassContents,
|
||||||
},
|
},
|
||||||
device::Queue,
|
device::Queue,
|
||||||
format::{ClearValue, Format},
|
format::{ClearColorValue, ClearValue, Format},
|
||||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
|
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
|
||||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter},
|
||||||
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
||||||
@@ -128,6 +128,16 @@ impl WCommandBuffer<CmdBufXfer> {
|
|||||||
Ok(image)
|
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(
|
pub fn update_image(
|
||||||
&mut self,
|
&mut self,
|
||||||
image: &Arc<Image>,
|
image: &Arc<Image>,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use cosmic_text::{FontSystem, SwashCache};
|
use cosmic_text::{FontSystem, SwashCache};
|
||||||
use etagere::{size2, Allocation, BucketedAtlasAllocator};
|
use etagere::{Allocation, BucketedAtlasAllocator, size2};
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc};
|
use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc};
|
||||||
@@ -8,20 +8,20 @@ use vulkano::{
|
|||||||
command_buffer::CommandBufferUsage,
|
command_buffer::CommandBufferUsage,
|
||||||
descriptor_set::DescriptorSet,
|
descriptor_set::DescriptorSet,
|
||||||
format::Format,
|
format::Format,
|
||||||
image::{view::ImageView, Image, ImageCreateInfo, ImageType, ImageUsage},
|
image::{Image, ImageCreateInfo, ImageType, ImageUsage, view::ImageView},
|
||||||
memory::allocator::AllocationCreateInfo,
|
memory::allocator::AllocationCreateInfo,
|
||||||
pipeline::graphics::vertex_input::Vertex,
|
pipeline::graphics::vertex_input::Vertex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
GlyphDetails, GpuCacheStatus,
|
||||||
custom_glyph::ContentType,
|
custom_glyph::ContentType,
|
||||||
shaders::{frag_atlas, vert_atlas},
|
shaders::{frag_atlas, vert_atlas},
|
||||||
text_renderer::GlyphonCacheKey,
|
text_renderer::GlyphonCacheKey,
|
||||||
GlyphDetails, GpuCacheStatus,
|
|
||||||
};
|
};
|
||||||
use crate::gfx::{
|
use crate::gfx::{
|
||||||
|
BLEND_ALPHA, WGfx,
|
||||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||||
WGfx, BLEND_ALPHA,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Pipeline & shaders to be reused between `TextRenderer` instances
|
/// Pipeline & shaders to be reused between `TextRenderer` instances
|
||||||
@@ -77,6 +77,8 @@ pub(super) struct InnerAtlas {
|
|||||||
common: TextPipeline,
|
common: TextPipeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const TEXT_ATLAS_ISLAND_PADDING_PX: u32 = 1;
|
||||||
|
|
||||||
impl InnerAtlas {
|
impl InnerAtlas {
|
||||||
const INITIAL_SIZE: u32 = 256;
|
const INITIAL_SIZE: u32 = 256;
|
||||||
|
|
||||||
@@ -131,10 +133,15 @@ impl InnerAtlas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn try_allocate(&mut self, width: usize, height: usize) -> Option<Allocation> {
|
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 {
|
loop {
|
||||||
let allocation = self.packer.allocate(size);
|
let allocation = self.packer.allocate(padded_size);
|
||||||
|
|
||||||
if allocation.is_some() {
|
if allocation.is_some() {
|
||||||
return allocation;
|
return allocation;
|
||||||
@@ -200,6 +207,9 @@ impl InnerAtlas {
|
|||||||
.gfx
|
.gfx
|
||||||
.create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
.create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||||
|
|
||||||
|
// clear newly allocated image with zeros
|
||||||
|
cmd_buf.clear_image(&image)?;
|
||||||
|
|
||||||
// Re-upload glyphs
|
// Re-upload glyphs
|
||||||
for (&cache_key, glyph) in &self.glyph_cache {
|
for (&cache_key, glyph) in &self.glyph_cache {
|
||||||
let (x, y) = match glyph.gpu_cache {
|
let (x, y) = match glyph.gpu_cache {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
gfx::{cmd::GfxCommandBuffer, pass::WGfxPass},
|
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::{
|
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 glam::{Mat4, Vec2, Vec3};
|
use glam::{Mat4, Vec2, Vec3};
|
||||||
use vulkano::{
|
use vulkano::{
|
||||||
buffer::{BufferUsage, Subbuffer},
|
buffer::{BufferUsage, Subbuffer},
|
||||||
@@ -359,14 +360,29 @@ fn prepare_glyph(
|
|||||||
|
|
||||||
inner = par.atlas.inner_for_content_mut(image.content_type);
|
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)?;
|
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(
|
cmd_buf.update_image(
|
||||||
inner.image_view.image(),
|
inner.image_view.image(),
|
||||||
&image.data,
|
&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]),
|
Some([image.width.into(), image.height.into(), 1]),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -374,8 +390,8 @@ fn prepare_glyph(
|
|||||||
|
|
||||||
(
|
(
|
||||||
GpuCacheStatus::InAtlas {
|
GpuCacheStatus::InAtlas {
|
||||||
x: atlas_min.x as u16,
|
x: atlas_glyph_min.x as u16,
|
||||||
y: atlas_min.y as u16,
|
y: atlas_glyph_min.y as u16,
|
||||||
content_type: image.content_type,
|
content_type: image.content_type,
|
||||||
},
|
},
|
||||||
Some(allocation.id),
|
Some(allocation.id),
|
||||||
|
|||||||
Reference in New Issue
Block a user