WIP: Add anti-aliasing to paths and render them after spriting them

This commit is contained in:
Antonio Scandurra 2021-03-30 16:37:12 +02:00
parent 98845663a9
commit 1903f63c45
4 changed files with 205 additions and 45 deletions

View file

@ -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)]