mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 05:15:00 +00:00
WIP: Add anti-aliasing to paths and render them after spriting them
This commit is contained in:
parent
98845663a9
commit
1903f63c45
4 changed files with 205 additions and 45 deletions
|
@ -65,12 +65,12 @@ impl Renderer {
|
|||
MTLResourceOptions::StorageModeManaged,
|
||||
);
|
||||
|
||||
let path_stencil_pixel_format = metal::MTLPixelFormat::Stencil8;
|
||||
let path_stencil_descriptor = metal::TextureDescriptor::new();
|
||||
path_stencil_descriptor.set_width(2048);
|
||||
path_stencil_descriptor.set_height(2048);
|
||||
path_stencil_descriptor.set_pixel_format(path_stencil_pixel_format);
|
||||
path_stencil_descriptor.set_usage(metal::MTLTextureUsage::RenderTarget);
|
||||
path_stencil_descriptor.set_width(1024);
|
||||
path_stencil_descriptor.set_height(768);
|
||||
path_stencil_descriptor.set_pixel_format(pixel_format);
|
||||
path_stencil_descriptor
|
||||
.set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
|
||||
path_stencil_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
|
||||
|
||||
let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts);
|
||||
|
@ -105,7 +105,7 @@ impl Renderer {
|
|||
"path_winding",
|
||||
"path_winding_vertex",
|
||||
"path_winding_fragment",
|
||||
path_stencil_pixel_format,
|
||||
pixel_format,
|
||||
)?;
|
||||
Ok(Self {
|
||||
device,
|
||||
|
@ -128,15 +128,21 @@ impl Renderer {
|
|||
output: &metal::TextureRef,
|
||||
) {
|
||||
let mut offset = 0;
|
||||
self.render_path_stencils(scene, &mut offset, drawable_size, command_buffer);
|
||||
self.render_layers(scene, &mut offset, drawable_size, command_buffer, output);
|
||||
let stencils = self.render_path_stencils(scene, &mut offset, command_buffer);
|
||||
self.render_layers(
|
||||
scene,
|
||||
stencils,
|
||||
&mut offset,
|
||||
drawable_size,
|
||||
command_buffer,
|
||||
output,
|
||||
);
|
||||
}
|
||||
|
||||
fn render_path_stencils(
|
||||
&mut self,
|
||||
scene: &Scene,
|
||||
offset: &mut usize,
|
||||
drawable_size: Vector2F,
|
||||
command_buffer: &metal::CommandBufferRef,
|
||||
) -> Vec<PathSprite> {
|
||||
let mut stencils = Vec::new();
|
||||
|
@ -145,16 +151,20 @@ impl Renderer {
|
|||
for (layer_id, layer) in scene.layers().iter().enumerate() {
|
||||
for path in layer.paths() {
|
||||
// Push a PathStencil struct for use later when sampling from the atlas as we draw the content of the layers
|
||||
let size = path.bounds.size().ceil().to_i32();
|
||||
let (atlas_id, atlas_origin) = self.path_stencils.allocate(size).unwrap();
|
||||
let origin = path.bounds.origin() * scene.scale_factor();
|
||||
let size = (path.bounds.size() * scene.scale_factor()).ceil();
|
||||
let (atlas_id, atlas_origin) =
|
||||
self.path_stencils.allocate(size.ceil().to_i32()).unwrap();
|
||||
let atlas_origin = atlas_origin.to_f32();
|
||||
stencils.push(PathSprite {
|
||||
layer_id,
|
||||
atlas_id,
|
||||
sprite: shaders::GPUISprite {
|
||||
origin: path.bounds.origin().to_float2(),
|
||||
origin: origin.to_float2(),
|
||||
size: size.to_float2(),
|
||||
atlas_origin: atlas_origin.to_float2(),
|
||||
color: path.color.to_uchar4(),
|
||||
compute_winding: 1,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -172,11 +182,10 @@ impl Renderer {
|
|||
|
||||
// Populate the vertices by translating them to their appropriate location in the atlas.
|
||||
for vertex in &path.vertices {
|
||||
let xy_position = (vertex.xy_position - path.bounds.origin())
|
||||
* scene.scale_factor()
|
||||
+ atlas_origin.to_f32();
|
||||
let xy_position =
|
||||
(vertex.xy_position - path.bounds.origin()) * scene.scale_factor();
|
||||
vertices.push(shaders::GPUIPathVertex {
|
||||
xy_position: xy_position.to_float2(),
|
||||
xy_position: (atlas_origin + xy_position).to_float2(),
|
||||
st_position: vertex.st_position.to_float2(),
|
||||
});
|
||||
}
|
||||
|
@ -205,25 +214,32 @@ impl Renderer {
|
|||
);
|
||||
|
||||
let render_pass_descriptor = metal::RenderPassDescriptor::new();
|
||||
|
||||
let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap();
|
||||
let color_attachment = render_pass_descriptor
|
||||
.color_attachments()
|
||||
.object_at(0)
|
||||
.unwrap();
|
||||
let stencil_texture = self.path_stencils.texture(atlas_id).unwrap();
|
||||
stencil_attachment.set_texture(Some(stencil_texture));
|
||||
stencil_attachment.set_load_action(metal::MTLLoadAction::Clear);
|
||||
stencil_attachment.set_store_action(metal::MTLStoreAction::Store);
|
||||
color_attachment.set_texture(Some(stencil_texture));
|
||||
color_attachment.set_load_action(metal::MTLLoadAction::Clear);
|
||||
color_attachment.set_store_action(metal::MTLStoreAction::Store);
|
||||
color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
|
||||
// let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap();
|
||||
// let stencil_texture = self.path_stencils.texture(atlas_id).unwrap();
|
||||
// stencil_attachment.set_texture(Some(stencil_texture));
|
||||
// stencil_attachment.set_load_action(metal::MTLLoadAction::Clear);
|
||||
// stencil_attachment.set_store_action(metal::MTLStoreAction::Store);
|
||||
|
||||
let stencil_descriptor = metal::DepthStencilDescriptor::new();
|
||||
let front_face_stencil = stencil_descriptor.front_face_stencil().unwrap();
|
||||
front_face_stencil.set_depth_stencil_pass_operation(metal::MTLStencilOperation::Invert);
|
||||
front_face_stencil.set_depth_failure_operation(metal::MTLStencilOperation::Keep);
|
||||
front_face_stencil.set_stencil_compare_function(metal::MTLCompareFunction::Always);
|
||||
front_face_stencil.set_read_mask(0x1);
|
||||
front_face_stencil.set_write_mask(0x1);
|
||||
let depth_stencil_state = self.device.new_depth_stencil_state(&stencil_descriptor);
|
||||
// let stencil_descriptor = metal::DepthStencilDescriptor::new();
|
||||
// let front_face_stencil = stencil_descriptor.front_face_stencil().unwrap();
|
||||
// front_face_stencil.set_depth_stencil_pass_operation(metal::MTLStencilOperation::Invert);
|
||||
// front_face_stencil.set_depth_failure_operation(metal::MTLStencilOperation::Keep);
|
||||
// front_face_stencil.set_stencil_compare_function(metal::MTLCompareFunction::Always);
|
||||
// front_face_stencil.set_read_mask(0x1);
|
||||
// front_face_stencil.set_write_mask(0x1);
|
||||
// let depth_stencil_state = self.device.new_depth_stencil_state(&stencil_descriptor);
|
||||
|
||||
let winding_command_encoder =
|
||||
command_buffer.new_render_command_encoder(render_pass_descriptor);
|
||||
winding_command_encoder.set_depth_stencil_state(&depth_stencil_state);
|
||||
winding_command_encoder.set_render_pipeline_state(&self.path_stencil_pipeline_state);
|
||||
winding_command_encoder.set_vertex_buffer(
|
||||
shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexVertices as u64,
|
||||
|
@ -264,6 +280,7 @@ impl Renderer {
|
|||
fn render_layers(
|
||||
&mut self,
|
||||
scene: &Scene,
|
||||
path_sprites: Vec<PathSprite>,
|
||||
offset: &mut usize,
|
||||
drawable_size: Vector2F,
|
||||
command_buffer: &metal::CommandBufferRef,
|
||||
|
@ -289,11 +306,20 @@ impl Renderer {
|
|||
zfar: 1.0,
|
||||
});
|
||||
|
||||
for layer in scene.layers() {
|
||||
for (layer_id, layer) in scene.layers().iter().enumerate() {
|
||||
self.clip(scene, layer, drawable_size, command_encoder);
|
||||
self.render_shadows(scene, layer, offset, drawable_size, command_encoder);
|
||||
self.render_quads(scene, layer, offset, drawable_size, command_encoder);
|
||||
self.render_sprites(scene, layer, offset, drawable_size, command_encoder);
|
||||
// TODO: Pass sprites relevant to this layer in a more efficient manner.
|
||||
self.render_path_sprites(
|
||||
scene,
|
||||
layer,
|
||||
path_sprites.iter().filter(|s| s.layer_id == layer_id),
|
||||
offset,
|
||||
drawable_size,
|
||||
command_encoder,
|
||||
);
|
||||
self.render_glyph_sprites(scene, layer, offset, drawable_size, command_encoder);
|
||||
}
|
||||
|
||||
command_encoder.end_encoding();
|
||||
|
@ -471,7 +497,7 @@ impl Renderer {
|
|||
);
|
||||
}
|
||||
|
||||
fn render_sprites(
|
||||
fn render_glyph_sprites(
|
||||
&mut self,
|
||||
scene: &Scene,
|
||||
layer: &Layer,
|
||||
|
@ -502,6 +528,7 @@ impl Renderer {
|
|||
size: sprite.size.to_float2(),
|
||||
atlas_origin: sprite.atlas_origin.to_float2(),
|
||||
color: glyph.color.to_uchar4(),
|
||||
compute_winding: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -563,6 +590,87 @@ impl Renderer {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_path_sprites<'a>(
|
||||
&mut self,
|
||||
scene: &Scene,
|
||||
layer: &Layer,
|
||||
sprites: impl Iterator<Item = &'a PathSprite>,
|
||||
offset: &mut usize,
|
||||
drawable_size: Vector2F,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) {
|
||||
let mut sprites = sprites.peekable();
|
||||
if sprites.peek().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut sprites_by_atlas = HashMap::new();
|
||||
for sprite in sprites {
|
||||
sprites_by_atlas
|
||||
.entry(sprite.atlas_id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(sprite.sprite);
|
||||
}
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
|
||||
Some(&self.unit_vertices),
|
||||
0,
|
||||
);
|
||||
command_encoder.set_vertex_bytes(
|
||||
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
|
||||
mem::size_of::<shaders::vector_float2>() as u64,
|
||||
[drawable_size.to_float2()].as_ptr() as *const c_void,
|
||||
);
|
||||
|
||||
for (atlas_id, sprites) in sprites_by_atlas {
|
||||
align_offset(offset);
|
||||
let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
|
||||
assert!(
|
||||
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||
"instance buffer exhausted"
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_buffer(
|
||||
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
|
||||
let texture = self.path_stencils.texture(atlas_id).unwrap();
|
||||
command_encoder.set_vertex_bytes(
|
||||
shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
|
||||
mem::size_of::<shaders::vector_float2>() as u64,
|
||||
[vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
|
||||
as *const c_void,
|
||||
);
|
||||
command_encoder.set_fragment_texture(
|
||||
shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
|
||||
Some(texture),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let buffer_contents = (self.instances.contents() as *mut u8)
|
||||
.offset(*offset as isize)
|
||||
as *mut shaders::GPUISprite;
|
||||
std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
|
||||
}
|
||||
self.instances.did_modify_range(NSRange {
|
||||
location: *offset as u64,
|
||||
length: (next_offset - *offset) as u64,
|
||||
});
|
||||
*offset = next_offset;
|
||||
|
||||
command_encoder.draw_primitives_instanced(
|
||||
metal::MTLPrimitiveType::Triangle,
|
||||
0,
|
||||
6,
|
||||
sprites.len() as u64,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn align_offset(offset: &mut usize) {
|
||||
|
@ -625,13 +733,47 @@ fn build_stencil_pipeline_state(
|
|||
descriptor.set_label(label);
|
||||
descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
|
||||
descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
|
||||
descriptor.set_stencil_attachment_pixel_format(pixel_format);
|
||||
let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
|
||||
color_attachment.set_pixel_format(pixel_format);
|
||||
color_attachment.set_blending_enabled(true);
|
||||
color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
|
||||
color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
|
||||
color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
|
||||
color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
|
||||
color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
|
||||
color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
|
||||
|
||||
device
|
||||
.new_render_pipeline_state(&descriptor)
|
||||
.map_err(|message| anyhow!("could not create render pipeline state: {}", message))
|
||||
}
|
||||
|
||||
// fn build_stencil_pipeline_state(
|
||||
// device: &metal::DeviceRef,
|
||||
// library: &metal::LibraryRef,
|
||||
// label: &str,
|
||||
// vertex_fn_name: &str,
|
||||
// fragment_fn_name: &str,
|
||||
// pixel_format: metal::MTLPixelFormat,
|
||||
// ) -> Result<metal::RenderPipelineState> {
|
||||
// let vertex_fn = library
|
||||
// .get_function(vertex_fn_name, None)
|
||||
// .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
|
||||
// let fragment_fn = library
|
||||
// .get_function(fragment_fn_name, None)
|
||||
// .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
|
||||
|
||||
// let descriptor = metal::RenderPipelineDescriptor::new();
|
||||
// descriptor.set_label(label);
|
||||
// descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
|
||||
// descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
|
||||
// descriptor.set_stencil_attachment_pixel_format(pixel_format);
|
||||
|
||||
// device
|
||||
// .new_render_pipeline_state(&descriptor)
|
||||
// .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
|
||||
// }
|
||||
|
||||
mod shaders {
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
|
|
@ -52,6 +52,7 @@ typedef struct {
|
|||
vector_float2 size;
|
||||
vector_float2 atlas_origin;
|
||||
vector_uchar4 color;
|
||||
uint8_t compute_winding;
|
||||
} GPUISprite;
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -168,6 +168,7 @@ struct SpriteFragmentInput {
|
|||
float4 position [[position]];
|
||||
float2 atlas_position;
|
||||
float4 color [[flat]];
|
||||
uchar compute_winding [[flat]];
|
||||
};
|
||||
|
||||
vertex SpriteFragmentInput sprite_vertex(
|
||||
|
@ -188,6 +189,7 @@ vertex SpriteFragmentInput sprite_vertex(
|
|||
device_position,
|
||||
atlas_position,
|
||||
coloru_to_colorf(sprite.color),
|
||||
sprite.compute_winding
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -197,8 +199,14 @@ fragment float4 sprite_fragment(
|
|||
) {
|
||||
constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
|
||||
float4 color = input.color;
|
||||
float4 mask = atlas.sample(atlas_sampler, input.atlas_position);
|
||||
color.a *= mask.a;
|
||||
float4 sample = atlas.sample(atlas_sampler, input.atlas_position);
|
||||
float mask;
|
||||
if (input.compute_winding) {
|
||||
mask = fmod(sample.r * 255., 2.);
|
||||
} else {
|
||||
mask = sample.a;
|
||||
}
|
||||
color.a *= mask;
|
||||
return color;
|
||||
}
|
||||
|
||||
|
@ -223,9 +231,14 @@ vertex PathWindingFragmentInput path_winding_vertex(
|
|||
fragment float4 path_winding_fragment(
|
||||
PathWindingFragmentInput input [[stage_in]]
|
||||
) {
|
||||
if (input.st_position.x * input.st_position.x - input.st_position.y > 0.) {
|
||||
return float4(0.);
|
||||
} else {
|
||||
return float4(1.);
|
||||
}
|
||||
float2 dx = dfdx(input.st_position);
|
||||
float2 dy = dfdy(input.st_position);
|
||||
float2 gradient = float2(
|
||||
(2. * input.st_position.x) * dx.x - dx.y,
|
||||
(2. * input.st_position.x) * dy.x - dy.y
|
||||
);
|
||||
float f = (input.st_position.x * input.st_position.x) - input.st_position.y;
|
||||
float distance = f / length(gradient);
|
||||
float alpha = saturate(0.5 - distance) / 255.;
|
||||
return float4(alpha, 0., 0., 1.);
|
||||
}
|
||||
|
|
|
@ -230,25 +230,29 @@ impl BufferElement {
|
|||
|
||||
let selection = Selection {
|
||||
line_height,
|
||||
start_y: row_range.start as f32 * line_height - scroll_top,
|
||||
start_y: bounds.origin_y() + row_range.start as f32 * line_height - scroll_top,
|
||||
lines: row_range
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let line_layout = &layout.line_layouts[(row - start_row) as usize];
|
||||
SelectionLine {
|
||||
start_x: if row == range_start.row() {
|
||||
line_layout.x_for_index(range_start.column() as usize)
|
||||
bounds.origin_x()
|
||||
+ line_layout.x_for_index(range_start.column() as usize)
|
||||
- scroll_left
|
||||
- descent
|
||||
} else {
|
||||
-scroll_left
|
||||
},
|
||||
end_x: if row == range_end.row() {
|
||||
line_layout.x_for_index(range_end.column() as usize)
|
||||
bounds.origin_x()
|
||||
+ line_layout.x_for_index(range_end.column() as usize)
|
||||
- scroll_left
|
||||
- descent
|
||||
} else {
|
||||
line_layout.width + corner_radius * 2.0 - scroll_left - descent
|
||||
bounds.origin_x() + line_layout.width + corner_radius * 2.0
|
||||
- scroll_left
|
||||
- descent
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue