Checkpoint
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user