mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-23 18:32:17 +00:00
WIP: Start on rendering glyphs
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
43abd96769
commit
764bfba2e2
9 changed files with 211 additions and 65 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -549,6 +549,25 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "etagere"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "520d7de540904fd09b11c03a47d50a7ce4ff37d1aa763f454fa60d9088ef8356"
|
||||||
|
dependencies = [
|
||||||
|
"euclid",
|
||||||
|
"svg_fmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "euclid"
|
||||||
|
version = "0.22.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51e5bac4ec41ece6346fd867815a57a221abdf48f4eb931b033789b5b4b6fc70"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "event-listener"
|
name = "event-listener"
|
||||||
version = "2.5.1"
|
version = "2.5.1"
|
||||||
|
@ -745,6 +764,7 @@ dependencies = [
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"core-text",
|
"core-text",
|
||||||
"ctor",
|
"ctor",
|
||||||
|
"etagere",
|
||||||
"font-kit",
|
"font-kit",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1483,6 +1503,12 @@ version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svg_fmt"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.60"
|
version = "1.0.60"
|
||||||
|
|
|
@ -7,6 +7,7 @@ version = "0.1.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-task = {git = "https://github.com/zedit-io/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"}
|
async-task = {git = "https://github.com/zedit-io/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"}
|
||||||
ctor = "0.1"
|
ctor = "0.1"
|
||||||
|
etagere = "0.2"
|
||||||
num_cpus = "1.13"
|
num_cpus = "1.13"
|
||||||
ordered-float = "2.1.1"
|
ordered-float = "2.1.1"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
|
|
|
@ -89,6 +89,8 @@ fn generate_shader_bindings() {
|
||||||
.whitelist_type("GPUIQuad")
|
.whitelist_type("GPUIQuad")
|
||||||
.whitelist_type("GPUIShadowInputIndex")
|
.whitelist_type("GPUIShadowInputIndex")
|
||||||
.whitelist_type("GPUIShadow")
|
.whitelist_type("GPUIShadow")
|
||||||
|
.whitelist_type("GPUISpriteInputIndex")
|
||||||
|
.whitelist_type("GPUISprite")
|
||||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||||
.generate()
|
.generate()
|
||||||
.expect("unable to generate bindings");
|
.expect("unable to generate bindings");
|
||||||
|
|
|
@ -4,6 +4,7 @@ mod event;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod runner;
|
mod runner;
|
||||||
|
mod sprite_cache;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use crate::platform;
|
use crate::platform;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{ffi::c_void, mem};
|
use std::{collections::HashMap, ffi::c_void, mem};
|
||||||
|
|
||||||
use self::shaders::ToUchar4;
|
use self::shaders::ToUchar4;
|
||||||
|
|
||||||
|
@ -15,8 +15,10 @@ const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decisio
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
quad_pipeline_state: metal::RenderPipelineState,
|
quad_pipeline_state: metal::RenderPipelineState,
|
||||||
shadow_pipeline_state: metal::RenderPipelineState,
|
shadow_pipeline_state: metal::RenderPipelineState,
|
||||||
|
sprite_pipeline_state: metal::RenderPipelineState,
|
||||||
unit_vertices: metal::Buffer,
|
unit_vertices: metal::Buffer,
|
||||||
instances: metal::Buffer,
|
instances: metal::Buffer,
|
||||||
|
sprite_cache: SpriteCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
|
@ -60,6 +62,14 @@ impl Renderer {
|
||||||
"shadow_fragment",
|
"shadow_fragment",
|
||||||
pixel_format,
|
pixel_format,
|
||||||
)?,
|
)?,
|
||||||
|
sprite_pipeline_state: build_pipeline_state(
|
||||||
|
device,
|
||||||
|
&library,
|
||||||
|
"sprite",
|
||||||
|
"sprite_vertex",
|
||||||
|
"sprite_fragment",
|
||||||
|
pixel_format,
|
||||||
|
)?,
|
||||||
unit_vertices,
|
unit_vertices,
|
||||||
instances,
|
instances,
|
||||||
})
|
})
|
||||||
|
@ -79,6 +89,7 @@ impl Renderer {
|
||||||
for layer in scene.layers() {
|
for layer in scene.layers() {
|
||||||
self.render_shadows(scene, layer, &mut offset, ctx);
|
self.render_shadows(scene, layer, &mut offset, ctx);
|
||||||
self.render_quads(scene, layer, &mut offset, ctx);
|
self.render_quads(scene, layer, &mut offset, ctx);
|
||||||
|
self.render_sprites(scene, layer, &mut offset, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +245,71 @@ impl Renderer {
|
||||||
layer.quads().len() as u64,
|
layer.quads().len() as u64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_sprites(
|
||||||
|
&mut self,
|
||||||
|
scene: &Scene,
|
||||||
|
layer: &Layer,
|
||||||
|
offset: &mut usize,
|
||||||
|
ctx: &RenderContext,
|
||||||
|
) {
|
||||||
|
if layer.glyphs().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
align_offset(offset);
|
||||||
|
let next_offset = *offset + layer.glyphs().len() * mem::size_of::<shaders::GPUISprite>();
|
||||||
|
assert!(
|
||||||
|
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||||
|
"instance buffer exhausted"
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut sprites = HashMap::new();
|
||||||
|
for glyph in layer.glyphs() {
|
||||||
|
let (atlas, bounds) =
|
||||||
|
self.sprite_cache
|
||||||
|
.rasterize_glyph(glyph.font_id, glyph.font_size, glyph.glyph_id);
|
||||||
|
sprites
|
||||||
|
.entry(atlas)
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(shaders::GPUISprite {
|
||||||
|
origin: glyph.origin.to_float2(),
|
||||||
|
size: bounds.size().to_float2(),
|
||||||
|
atlas_origin: bounds.origin().to_float2(),
|
||||||
|
color: glyph.color.to_uchar4(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.command_encoder
|
||||||
|
.set_render_pipeline_state(&self.sprite_pipeline_state);
|
||||||
|
ctx.command_encoder.set_vertex_buffer(
|
||||||
|
shaders::GPUISpriteInputIndex_GPUISpriteInputIndexVertices as u64,
|
||||||
|
Some(&self.unit_vertices),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
ctx.command_encoder.set_vertex_buffer(
|
||||||
|
shaders::GPUISpriteInputIndex_GPUISpriteInputIndexSprites as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
ctx.command_encoder.set_vertex_bytes(
|
||||||
|
shaders::GPUISpriteInputIndex_GPUISpriteInputIndexUniforms as u64,
|
||||||
|
mem::size_of::<shaders::GPUIUniforms>() as u64,
|
||||||
|
[shaders::GPUIUniforms {
|
||||||
|
viewport_size: ctx.drawable_size.to_float2(),
|
||||||
|
}]
|
||||||
|
.as_ptr() as *const c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
let buffer_contents = unsafe {
|
||||||
|
(self.instances.contents() as *mut u8).offset(*offset as isize)
|
||||||
|
as *mut shaders::GPUISprite
|
||||||
|
};
|
||||||
|
|
||||||
|
for glyph in layer.glyphs() {
|
||||||
|
let sprite = self.sprite_cache.rasterize_glyph();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align_offset(offset: &mut usize) {
|
fn align_offset(offset: &mut usize) {
|
||||||
|
|
|
@ -35,3 +35,16 @@ typedef struct {
|
||||||
float sigma;
|
float sigma;
|
||||||
vector_uchar4 color;
|
vector_uchar4 color;
|
||||||
} GPUIShadow;
|
} GPUIShadow;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GPUISpriteInputIndexVertices = 0,
|
||||||
|
GPUISpriteInputIndexSprites = 1,
|
||||||
|
GPUISpriteInputIndexUniforms = 2,
|
||||||
|
} GPUISpriteInputIndex;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
vector_float2 origin;
|
||||||
|
vector_float2 size;
|
||||||
|
vector_float2 atlas_origin;
|
||||||
|
vector_uchar4 color;
|
||||||
|
} GPUISprite;
|
||||||
|
|
19
gpui/src/platform/mac/sprite_cache.rs
Normal file
19
gpui/src/platform/mac/sprite_cache.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::geometry::vector::Vector2I;
|
||||||
|
use etagere::BucketedAtlasAllocator;
|
||||||
|
|
||||||
|
struct SpriteCache {
|
||||||
|
atlasses: Vec<etagere::BucketedAtlasAllocator>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteCache {
|
||||||
|
fn new(size: Vector2I) -> Self {
|
||||||
|
let size = etagere::Size::new(size.x(), size.y());
|
||||||
|
Self {
|
||||||
|
atlasses: vec![BucketedAtlasAllocator::new(size)],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_glyph(&mut self) {
|
||||||
|
self.atlasses.last().unwrap()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
use core::f32;
|
use crate::{
|
||||||
|
color::ColorU,
|
||||||
|
fonts::{FontId, GlyphId},
|
||||||
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{color::ColorU, geometry::rect::RectF};
|
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
layers: Vec<Layer>,
|
layers: Vec<Layer>,
|
||||||
|
@ -12,6 +15,7 @@ pub struct Layer {
|
||||||
clip_bounds: Option<RectF>,
|
clip_bounds: Option<RectF>,
|
||||||
quads: Vec<Quad>,
|
quads: Vec<Quad>,
|
||||||
shadows: Vec<Shadow>,
|
shadows: Vec<Shadow>,
|
||||||
|
glyphs: Vec<Glyph>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -30,6 +34,15 @@ pub struct Shadow {
|
||||||
pub color: ColorU,
|
pub color: ColorU,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Glyph {
|
||||||
|
pub font_id: FontId,
|
||||||
|
pub font_size: f32,
|
||||||
|
pub glyph_id: GlyphId,
|
||||||
|
pub origin: Vector2F,
|
||||||
|
pub color: ColorU,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug)]
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
pub struct Border {
|
pub struct Border {
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
|
@ -76,6 +89,10 @@ impl Scene {
|
||||||
self.active_layer().push_shadow(shadow)
|
self.active_layer().push_shadow(shadow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_glyph(&mut self, glyph: Glyph) {
|
||||||
|
self.active_layer().push_glyph(glyph)
|
||||||
|
}
|
||||||
|
|
||||||
fn active_layer(&mut self) -> &mut Layer {
|
fn active_layer(&mut self) -> &mut Layer {
|
||||||
&mut self.layers[*self.active_layer_stack.last().unwrap()]
|
&mut self.layers[*self.active_layer_stack.last().unwrap()]
|
||||||
}
|
}
|
||||||
|
@ -97,6 +114,14 @@ impl Layer {
|
||||||
pub fn shadows(&self) -> &[Shadow] {
|
pub fn shadows(&self) -> &[Shadow] {
|
||||||
self.shadows.as_slice()
|
self.shadows.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_glyph(&mut self, glyph: Glyph) {
|
||||||
|
self.glyphs.push(glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn glyphs(&self) -> &[Glyph] {
|
||||||
|
self.glyphs.as_slice()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Border {
|
impl Border {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
color::ColorU,
|
color::ColorU,
|
||||||
fonts::{FontCache, FontId, GlyphId},
|
fonts::{FontCache, FontId, GlyphId},
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
PaintContext,
|
scene, PaintContext,
|
||||||
};
|
};
|
||||||
use core_foundation::{
|
use core_foundation::{
|
||||||
attributed_string::CFMutableAttributedString,
|
attributed_string::CFMutableAttributedString,
|
||||||
|
@ -186,71 +186,54 @@ impl Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(
|
pub fn paint(&self, bounds: RectF, colors: &[(Range<usize>, ColorU)], ctx: &mut PaintContext) {
|
||||||
&self,
|
let mut colors = colors.iter().peekable();
|
||||||
_bounds: RectF,
|
let mut color = ColorU::black();
|
||||||
_colors: &[(Range<usize>, ColorU)],
|
|
||||||
_ctx: &mut PaintContext,
|
|
||||||
) {
|
|
||||||
// canvas.set_font_size(self.font_size);
|
|
||||||
// let mut colors = colors.iter().peekable();
|
|
||||||
|
|
||||||
// for run in &self.runs {
|
for run in &self.runs {
|
||||||
// let bounding_box = font_cache.bounding_box(run.font_id, self.font_size);
|
let bounding_box = ctx.font_cache.bounding_box(run.font_id, self.font_size);
|
||||||
// let ascent = font_cache.scale_metric(
|
let ascent = ctx.font_cache.scale_metric(
|
||||||
// font_cache.metric(run.font_id, |m| m.ascent),
|
ctx.font_cache.metric(run.font_id, |m| m.ascent),
|
||||||
// run.font_id,
|
run.font_id,
|
||||||
// self.font_size,
|
self.font_size,
|
||||||
// );
|
);
|
||||||
// let descent = font_cache.scale_metric(
|
let descent = ctx.font_cache.scale_metric(
|
||||||
// font_cache.metric(run.font_id, |m| m.descent),
|
ctx.font_cache.metric(run.font_id, |m| m.descent),
|
||||||
// run.font_id,
|
run.font_id,
|
||||||
// self.font_size,
|
self.font_size,
|
||||||
// );
|
);
|
||||||
|
|
||||||
// let max_glyph_width = bounding_box.x();
|
let max_glyph_width = bounding_box.x();
|
||||||
// let font = font_cache.font(run.font_id);
|
let font = ctx.font_cache.font(run.font_id);
|
||||||
// let font_name = font_cache.font_name(run.font_id);
|
let font_name = ctx.font_cache.font_name(run.font_id);
|
||||||
// let is_emoji = font_cache.is_emoji(run.font_id);
|
let is_emoji = ctx.font_cache.is_emoji(run.font_id);
|
||||||
// for glyph in &run.glyphs {
|
for glyph in &run.glyphs {
|
||||||
// let glyph_origin = origin + glyph.position - vec2f(0.0, descent);
|
let glyph_origin = bounds.origin() + glyph.position;
|
||||||
|
if glyph_origin.x() + max_glyph_width < bounds.origin().x() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if glyph_origin.x() > bounds.upper_right().x() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// if glyph_origin.x() + max_glyph_width < viewport_rect.origin().x() {
|
while let Some((range, next_color)) = colors.peek() {
|
||||||
// continue;
|
if glyph.index >= range.end {
|
||||||
// }
|
colors.next();
|
||||||
|
} else {
|
||||||
|
color = *next_color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if glyph_origin.x() > viewport_rect.upper_right().x() {
|
ctx.scene.push_glyph(scene::Glyph {
|
||||||
// break;
|
font_id: run.font_id,
|
||||||
// }
|
font_size: self.font_size,
|
||||||
|
glyph_id: glyph.id,
|
||||||
// while let Some((range, color)) = colors.peek() {
|
origin: glyph_origin,
|
||||||
// if glyph.index >= range.end {
|
color,
|
||||||
// colors.next();
|
});
|
||||||
// } else {
|
}
|
||||||
// if glyph.index == range.start {
|
}
|
||||||
// canvas.set_fill_style(FillStyle::Color(*color));
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if is_emoji {
|
|
||||||
// match font_cache.render_emoji(glyph.id, self.font_size) {
|
|
||||||
// Ok(image) => {
|
|
||||||
// canvas.draw_image(image, RectF::new(glyph_origin, bounding_box));
|
|
||||||
// }
|
|
||||||
// Err(error) => log::error!("rasterizing emoji: {}", error),
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// canvas.fill_glyph(
|
|
||||||
// &font,
|
|
||||||
// &font_name,
|
|
||||||
// glyph.id,
|
|
||||||
// glyph_origin + vec2f(0.0, ascent),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue