Extract image rasterization into ImageCache

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-09-14 18:11:59 +02:00
parent d15eda53f6
commit bd4d73bb27
3 changed files with 58 additions and 42 deletions

View file

@ -3,6 +3,7 @@ mod dispatcher;
mod event;
mod fonts;
mod geometry;
mod image_cache;
mod platform;
mod renderer;
mod sprite_cache;

View file

@ -0,0 +1,49 @@
use metal::{MTLPixelFormat, TextureDescriptor, TextureRef};
use super::atlas::{AllocId, AtlasAllocator};
use crate::{
geometry::{rect::RectI, vector::Vector2I},
ImageData,
};
use std::{collections::HashMap, mem};
pub struct ImageCache {
prev_frame: HashMap<usize, (AllocId, RectI)>,
curr_frame: HashMap<usize, (AllocId, RectI)>,
atlases: AtlasAllocator,
}
impl ImageCache {
pub fn new(device: metal::Device, size: Vector2I) -> Self {
let descriptor = TextureDescriptor::new();
descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
descriptor.set_width(size.x() as u64);
descriptor.set_height(size.y() as u64);
Self {
prev_frame: Default::default(),
curr_frame: Default::default(),
atlases: AtlasAllocator::new(device, descriptor),
}
}
pub fn render(&mut self, image: &ImageData) -> (AllocId, RectI) {
let (alloc_id, atlas_bounds) = self
.prev_frame
.remove(&image.id)
.or_else(|| self.curr_frame.get(&image.id).copied())
.unwrap_or_else(|| self.atlases.upload(image.size(), image.as_bytes()));
self.curr_frame.insert(image.id, (alloc_id, atlas_bounds));
(alloc_id, atlas_bounds)
}
pub fn finish_frame(&mut self) {
mem::swap(&mut self.prev_frame, &mut self.curr_frame);
for (_, (id, _)) in self.curr_frame.drain() {
self.atlases.deallocate(id);
}
}
pub fn atlas_texture(&self, atlas_id: usize) -> Option<&TextureRef> {
self.atlases.texture(atlas_id)
}
}

View file

@ -1,11 +1,8 @@
use super::{
atlas::{self, AtlasAllocator},
sprite_cache::SpriteCache,
};
use super::{atlas::AtlasAllocator, image_cache::ImageCache, sprite_cache::SpriteCache};
use crate::{
color::Color,
geometry::{
rect::{RectF, RectI},
rect::RectF,
vector::{vec2f, vec2i, Vector2F},
},
platform,
@ -22,10 +19,8 @@ const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decisio
pub struct Renderer {
sprite_cache: SpriteCache,
image_cache: ImageCache,
path_atlases: AtlasAllocator,
image_atlases: AtlasAllocator,
prev_rendered_images: HashMap<usize, (atlas::AllocId, RectI)>,
curr_rendered_images: HashMap<usize, (atlas::AllocId, RectI)>,
quad_pipeline_state: metal::RenderPipelineState,
shadow_pipeline_state: metal::RenderPipelineState,
sprite_pipeline_state: metal::RenderPipelineState,
@ -70,10 +65,9 @@ impl Renderer {
);
let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts);
let image_cache = ImageCache::new(device.clone(), vec2i(1024, 768));
let path_atlases =
AtlasAllocator::new(device.clone(), build_path_atlas_texture_descriptor());
let image_atlases =
AtlasAllocator::new(device.clone(), build_image_atlas_texture_descriptor());
let quad_pipeline_state = build_pipeline_state(
&device,
&library,
@ -116,10 +110,8 @@ impl Renderer {
);
Self {
sprite_cache,
image_cache,
path_atlases,
image_atlases,
prev_rendered_images: Default::default(),
curr_rendered_images: Default::default(),
quad_pipeline_state,
shadow_pipeline_state,
sprite_pipeline_state,
@ -139,11 +131,6 @@ impl Renderer {
) {
let mut offset = 0;
mem::swap(
&mut self.curr_rendered_images,
&mut self.prev_rendered_images,
);
let path_sprites = self.render_path_atlases(scene, &mut offset, command_buffer);
self.render_layers(
scene,
@ -157,11 +144,7 @@ impl Renderer {
location: 0,
length: offset as NSUInteger,
});
for (id, _) in self.prev_rendered_images.values() {
self.image_atlases.deallocate(*id);
}
self.prev_rendered_images.clear();
self.image_cache.finish_frame();
}
fn render_path_atlases(
@ -660,16 +643,7 @@ impl Renderer {
let target_size = image.bounds.size() * scale_factor;
let corner_radius = image.corner_radius * scale_factor;
let border_width = image.border.width * scale_factor;
let (alloc_id, atlas_bounds) = self
.prev_rendered_images
.remove(&image.data.id)
.or_else(|| self.curr_rendered_images.get(&image.data.id).copied())
.unwrap_or_else(|| {
self.image_atlases
.upload(image.data.size(), image.data.as_bytes())
});
self.curr_rendered_images
.insert(image.data.id, (alloc_id, atlas_bounds));
let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
images_by_atlas
.entry(alloc_id.atlas_id)
.or_insert_with(Vec::new)
@ -707,7 +681,7 @@ impl Renderer {
"instance buffer exhausted"
);
let texture = self.image_atlases.texture(atlas_id).unwrap();
let texture = self.image_cache.atlas_texture(atlas_id).unwrap();
command_encoder.set_vertex_buffer(
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexImages as u64,
Some(&self.instances),
@ -858,14 +832,6 @@ fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
texture_descriptor
}
fn build_image_atlas_texture_descriptor() -> metal::TextureDescriptor {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_width(2048);
texture_descriptor.set_height(2048);
texture_descriptor.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
texture_descriptor
}
fn align_offset(offset: &mut usize) {
let r = *offset % 256;
if r > 0 {