From bd4d73bb2714f9ad4d41110c4e7b67c08612408d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 14 Sep 2021 18:11:59 +0200 Subject: [PATCH] Extract image rasterization into `ImageCache` Co-Authored-By: Nathan Sobo --- gpui/src/platform/mac.rs | 1 + gpui/src/platform/mac/image_cache.rs | 49 +++++++++++++++++++++++++++ gpui/src/platform/mac/renderer.rs | 50 +++++----------------------- 3 files changed, 58 insertions(+), 42 deletions(-) create mode 100644 gpui/src/platform/mac/image_cache.rs diff --git a/gpui/src/platform/mac.rs b/gpui/src/platform/mac.rs index 016d3cb4448b1034f5b56b96140e62e13e027476..8cf3f62874aac87bc6d1841fdfc79d7914a66b50 100644 --- a/gpui/src/platform/mac.rs +++ b/gpui/src/platform/mac.rs @@ -3,6 +3,7 @@ mod dispatcher; mod event; mod fonts; mod geometry; +mod image_cache; mod platform; mod renderer; mod sprite_cache; diff --git a/gpui/src/platform/mac/image_cache.rs b/gpui/src/platform/mac/image_cache.rs new file mode 100644 index 0000000000000000000000000000000000000000..dac2e1a38b24051e983f54152a6e57c2926819b3 --- /dev/null +++ b/gpui/src/platform/mac/image_cache.rs @@ -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, + curr_frame: HashMap, + 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) + } +} diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index d650c2d9170ee98d6fdad9fe11d1040b1900ee04..369696d47838d25afc105dcfa2426e905c19b5c2 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -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, - curr_rendered_images: HashMap, 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 {