Checkpoint

This commit is contained in:
Antonio Scandurra
2023-10-09 19:13:57 +02:00
parent 6a4c2a0d40
commit d889cdecde
8 changed files with 484 additions and 279 deletions
+75 -43
View File
@@ -1,14 +1,14 @@
use std::borrow::Cow;
use crate::{
AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size,
AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
Point, Size,
};
use anyhow::{anyhow, Result};
use anyhow::Result;
use collections::HashMap;
use derive_more::{Deref, DerefMut};
use etagere::BucketedAtlasAllocator;
use metal::Device;
use parking_lot::Mutex;
use std::borrow::Cow;
pub struct MetalAtlas(Mutex<MetalAtlasState>);
@@ -16,19 +16,31 @@ impl MetalAtlas {
pub fn new(device: Device) -> Self {
MetalAtlas(Mutex::new(MetalAtlasState {
device: AssertSend(device),
textures: Default::default(),
monochrome_textures: Default::default(),
polychrome_textures: Default::default(),
path_textures: Default::default(),
tiles_by_key: Default::default(),
}))
}
pub(crate) fn texture(&self, id: AtlasTextureId) -> metal::Texture {
self.0.lock().textures[id.0 as usize].metal_texture.clone()
pub(crate) fn metal_texture(&self, id: AtlasTextureId) -> metal::Texture {
self.0.lock().texture(id).metal_texture.clone()
}
pub(crate) fn allocate(
&self,
size: Size<DevicePixels>,
texture_kind: AtlasTextureKind,
) -> AtlasTile {
self.0.lock().allocate(size, texture_kind)
}
}
struct MetalAtlasState {
device: AssertSend<Device>,
textures: Vec<MetalAtlasTexture>,
monochrome_textures: Vec<MetalAtlasTexture>,
polychrome_textures: Vec<MetalAtlasTexture>,
path_textures: Vec<MetalAtlasTexture>,
tiles_by_key: HashMap<AtlasKey, AtlasTile>,
}
@@ -43,23 +55,9 @@ impl PlatformAtlas for MetalAtlas {
return Ok(tile.clone());
} else {
let (size, bytes) = build()?;
let tile = lock
.textures
.iter_mut()
.rev()
.find_map(|texture| {
if texture.monochrome == key.is_monochrome() {
texture.upload(size, &bytes)
} else {
None
}
})
.or_else(|| {
let texture = lock.push_texture(size, key.is_monochrome());
texture.upload(size, &bytes)
})
.ok_or_else(|| anyhow!("could not allocate in new texture"))?;
lock.tiles_by_key.insert(key.clone(), tile.clone());
let tile = lock.allocate(size, key.texture_kind());
let texture = lock.texture(tile.texture_id);
texture.upload(tile.bounds, &bytes);
Ok(tile)
}
}
@@ -70,10 +68,26 @@ impl PlatformAtlas for MetalAtlas {
}
impl MetalAtlasState {
fn allocate(&mut self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
let textures = match texture_kind {
AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
AtlasTextureKind::Path => &mut self.path_textures,
};
textures
.iter_mut()
.rev()
.find_map(|texture| texture.allocate(size))
.unwrap_or_else(|| {
let texture = self.push_texture(size, texture_kind);
texture.allocate(size).unwrap()
})
}
fn push_texture(
&mut self,
min_size: Size<DevicePixels>,
monochrome: bool,
kind: AtlasTextureKind,
) -> &mut MetalAtlasTexture {
const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {
width: DevicePixels(1024),
@@ -84,21 +98,38 @@ impl MetalAtlasState {
let texture_descriptor = metal::TextureDescriptor::new();
texture_descriptor.set_width(size.width.into());
texture_descriptor.set_height(size.height.into());
if monochrome {
texture_descriptor.set_pixel_format(metal::MTLPixelFormat::A8Unorm);
} else {
texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
}
let pixel_format = match kind {
AtlasTextureKind::Monochrome => metal::MTLPixelFormat::A8Unorm,
AtlasTextureKind::Polychrome => metal::MTLPixelFormat::BGRA8Unorm,
AtlasTextureKind::Path => metal::MTLPixelFormat::R16Float,
};
texture_descriptor.set_pixel_format(pixel_format);
let metal_texture = self.device.new_texture(&texture_descriptor);
let textures = match kind {
AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
AtlasTextureKind::Path => &mut self.path_textures,
};
let atlas_texture = MetalAtlasTexture {
id: AtlasTextureId(self.textures.len() as u32),
id: AtlasTextureId {
index: textures.len() as u32,
kind,
},
allocator: etagere::BucketedAtlasAllocator::new(size.into()),
metal_texture: AssertSend(metal_texture),
monochrome,
};
self.textures.push(atlas_texture);
self.textures.last_mut().unwrap()
textures.push(atlas_texture);
textures.last_mut().unwrap()
}
fn texture(&self, id: AtlasTextureId) -> &MetalAtlasTexture {
let textures = match id.kind {
crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
crate::AtlasTextureKind::Path => &self.path_textures,
};
&textures[id.index as usize]
}
}
@@ -106,11 +137,10 @@ struct MetalAtlasTexture {
id: AtlasTextureId,
allocator: BucketedAtlasAllocator,
metal_texture: AssertSend<metal::Texture>,
monochrome: bool,
}
impl MetalAtlasTexture {
fn upload(&mut self, size: Size<DevicePixels>, bytes: &[u8]) -> Option<AtlasTile> {
fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
let allocation = self.allocator.allocate(size.into())?;
let tile = AtlasTile {
texture_id: self.id,
@@ -120,20 +150,22 @@ impl MetalAtlasTexture {
size,
},
};
Some(tile)
}
fn upload(&self, bounds: Bounds<DevicePixels>, bytes: &[u8]) {
let region = metal::MTLRegion::new_2d(
tile.bounds.origin.x.into(),
tile.bounds.origin.y.into(),
tile.bounds.size.width.into(),
tile.bounds.size.height.into(),
bounds.origin.x.into(),
bounds.origin.y.into(),
bounds.size.width.into(),
bounds.size.height.into(),
);
self.metal_texture.replace_region(
region,
0,
bytes.as_ptr() as *const _,
u32::from(tile.bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64,
u32::from(bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64,
);
Some(tile)
}
fn bytes_per_pixel(&self) -> u8 {
@@ -1,12 +1,14 @@
use crate::{
point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite,
PrimitiveBatch, Quad, Scene, Shadow, Size, Underline,
point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, DevicePixels, MetalAtlas,
MonochromeSprite, PathId, PolychromeSprite, PrimitiveBatch, Quad, Scene, Shadow, Size,
Underline,
};
use cocoa::{
base::{NO, YES},
foundation::NSUInteger,
quartzcore::AutoresizingMask,
};
use collections::HashMap;
use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
use objc::{self, msg_send, sel, sel_impl};
use std::{ffi::c_void, mem, ptr, sync::Arc};
@@ -14,7 +16,7 @@ use std::{ffi::c_void, mem, ptr, sync::Arc};
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
pub struct MetalRenderer {
pub(crate) struct MetalRenderer {
layer: metal::MetalLayer,
command_queue: CommandQueue,
shadows_pipeline_state: metal::RenderPipelineState,
@@ -150,7 +152,7 @@ impl MetalRenderer {
&self.sprite_atlas
}
pub fn draw(&mut self, scene: &mut Scene) {
pub fn draw(&mut self, scene: &Scene) {
let layer = self.layer.clone();
let viewport_size = layer.drawable_size();
let viewport_size: Size<DevicePixels> = size(
@@ -192,6 +194,15 @@ impl MetalRenderer {
});
let mut instance_offset = 0;
let mut path_tiles: HashMap<PathId, AtlasTile> = HashMap::default();
for path in scene.paths() {
let tile = self
.sprite_atlas
.allocate(path.bounds.size.map(Into::into), AtlasTextureKind::Path);
path_tiles.insert(path.id, tile);
}
for batch in scene.batches() {
match batch {
PrimitiveBatch::Shadows(shadows) => {
@@ -205,6 +216,9 @@ impl MetalRenderer {
PrimitiveBatch::Quads(quads) => {
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
}
PrimitiveBatch::Paths(paths) => {
// self.draw_paths(paths, &mut instance_offset, viewport_size, command_encoder);
}
PrimitiveBatch::Underlines(underlines) => {
self.draw_underlines(
underlines,
@@ -441,7 +455,7 @@ impl MetalRenderer {
}
align_offset(offset);
let texture = self.sprite_atlas.texture(texture_id);
let texture = self.sprite_atlas.metal_texture(texture_id);
let texture_size = size(
DevicePixels(texture.width() as i32),
DevicePixels(texture.height() as i32),
@@ -512,7 +526,7 @@ impl MetalRenderer {
}
align_offset(offset);
let texture = self.sprite_atlas.texture(texture_id);
let texture = self.sprite_atlas.metal_texture(texture_id);
let texture_size = size(
DevicePixels(texture.width() as i32),
DevicePixels(texture.height() as i32),
+3 -3
View File
@@ -911,7 +911,7 @@ impl PlatformWindow for MacWindow {
}
}
fn draw(&self, scene: crate::Scene) {
fn draw(&self, scene: Scene) {
let mut this = self.0.lock();
this.scene_to_render = Some(scene);
unsafe {
@@ -1395,8 +1395,8 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
unsafe {
let window_state = get_window_state(this);
let mut window_state = window_state.as_ref().lock();
if let Some(mut scene) = window_state.scene_to_render.take() {
window_state.renderer.draw(&mut scene);
if let Some(scene) = window_state.scene_to_render.take() {
window_state.renderer.draw(&scene);
}
}
}