From c16f526abae03d056b3fd47a34e2af0c94ab63ed Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 5 Jan 2016 16:11:17 -0800 Subject: [PATCH] Use 4x MSAA in the border corner shader, and draw border corners at device resolution. Closes #58. Closes #104. --- res/border.fs.glsl | 35 ++++++++++++++++++++--------------- src/batch_builder.rs | 40 ++++++++++++++++++++++++++-------------- src/node_compiler.rs | 3 ++- src/renderer.rs | 38 +++++++++++++++++++++++++++----------- src/resource_cache.rs | 4 ++++ src/resource_list.rs | 12 ++++++++---- src/tessellator.rs | 9 ++++++--- src/texture_cache.rs | 13 ++++++++----- 8 files changed, 101 insertions(+), 53 deletions(-) diff --git a/res/border.fs.glsl b/res/border.fs.glsl index 1d60109378..272e7370e8 100644 --- a/res/border.fs.glsl +++ b/res/border.fs.glsl @@ -7,26 +7,31 @@ */ -void main(void) -{ - float h = vBorderPosition.x; - float k = vBorderPosition.y; +float Value(vec2 position) { float outer_rx = vBorderRadii.x; float outer_ry = vBorderRadii.y; + float outer_dx = position.x * position.x / (outer_rx * outer_rx); + float outer_dy = position.y * position.y / (outer_ry * outer_ry); + if (outer_dx + outer_dy > 1.0) + return 0.0; + float inner_rx = vBorderRadii.z; float inner_ry = vBorderRadii.w; + if (inner_rx == 0.0 || inner_ry == 0.0) + return 1.0; - float outer_dx = ((vPosition.x - h) * (vPosition.x - h)) / (outer_rx * outer_rx); - float outer_dy = ((vPosition.y - k) * (vPosition.y - k)) / (outer_ry * outer_ry); - - float inner_dx = ((vPosition.x - h) * (vPosition.x - h)) / (inner_rx * inner_rx); - float inner_dy = ((vPosition.y - k) * (vPosition.y - k)) / (inner_ry * inner_ry); + float inner_dx = position.x * position.x / (inner_rx * inner_rx); + float inner_dy = position.y * position.y / (inner_ry * inner_ry); + return inner_dx + inner_dy >= 1.0 ? 1.0 : 0.0; +} - if ((outer_dx + outer_dy <= 1.0) && - (inner_dx + inner_dy >= 1.0)) { - SetFragColor(vColor); - } else { - SetFragColor(vec4(1.0) - vColor); - } +void main(void) +{ + vec2 position = vPosition - vBorderPosition.xy; + vec4 pixelBounds = vec4(floor(position.x), floor(position.y), + ceil(position.x), ceil(position.y)); + float value = (Value(pixelBounds.xy) + Value(pixelBounds.zy) + + Value(pixelBounds.xw) + Value(pixelBounds.zw)) / 4.0; + SetFragColor(mix(vec4(1.0) - vColor, vColor, value)); } diff --git a/src/batch_builder.rs b/src/batch_builder.rs index 69ff677f9f..1f76bb1cca 100644 --- a/src/batch_builder.rs +++ b/src/batch_builder.rs @@ -1069,7 +1069,8 @@ impl<'a> BatchBuilder<'a> { inner_radius: &Size2D, resource_cache: &ResourceCache, clip_buffers: &mut clipper::ClipBuffers, - rotation_angle: BasicRotationAngle) { + rotation_angle: BasicRotationAngle, + device_pixel_ratio: f32) { if color0.a <= 0.0 && color1.a <= 0.0 { return } @@ -1107,7 +1108,8 @@ impl<'a> BatchBuilder<'a> { inner_radius, resource_cache, clip_buffers, - rotation_angle); + rotation_angle, + device_pixel_ratio); self.add_solid_border_corner(clip, &inner_corner_rect, radius_extent, @@ -1117,7 +1119,8 @@ impl<'a> BatchBuilder<'a> { inner_radius, resource_cache, clip_buffers, - rotation_angle); + rotation_angle, + device_pixel_ratio); // Draw the solid parts: if util::rect_is_well_formed_and_nonempty(&color0_rect) { @@ -1145,7 +1148,8 @@ impl<'a> BatchBuilder<'a> { inner_radius, resource_cache, clip_buffers, - rotation_angle) + rotation_angle, + device_pixel_ratio) } } } @@ -1160,7 +1164,8 @@ impl<'a> BatchBuilder<'a> { inner_radius: &Size2D, resource_cache: &ResourceCache, clip_buffers: &mut clipper::ClipBuffers, - rotation_angle: BasicRotationAngle) { + rotation_angle: BasicRotationAngle, + device_pixel_ratio: f32) { // TODO: Check for zero width/height borders! // FIXME(pcwalton): It's kind of messy to be matching on the rotation angle here to pick // the right rect to draw the rounded corner in. Is there a more elegant way to do this? @@ -1170,11 +1175,13 @@ impl<'a> BatchBuilder<'a> { let dummy_mask_image = resource_cache.get_dummy_mask_image(); // Draw the rounded part of the corner. - for rect_index in 0..tessellator::quad_count_for_border_corner(outer_radius) { + for rect_index in 0..tessellator::quad_count_for_border_corner(outer_radius, + device_pixel_ratio) { let tessellated_rect = outer_corner_rect.tessellate_border_corner(outer_radius, - inner_radius, - rotation_angle, - rect_index); + inner_radius, + device_pixel_ratio, + rotation_angle, + rect_index); let mask_image = match BorderRadiusRasterOp::create(outer_radius, inner_radius, false, @@ -1358,7 +1365,8 @@ impl<'a> BatchBuilder<'a> { clip: &CombinedClipRegion, info: &BorderDisplayItem, resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { + clip_buffers: &mut ClipBuffers, + device_pixel_ratio: f32) { // TODO: If any border segment is alpha, place all in alpha pass. // Is it ever worth batching at a per-segment level? let radius = &info.radius; @@ -1442,7 +1450,8 @@ impl<'a> BatchBuilder<'a> { &info.top_left_inner_radius(), resource_cache, clip_buffers, - BasicRotationAngle::Upright); + BasicRotationAngle::Upright, + device_pixel_ratio); self.add_border_corner(clip, info.top.style, @@ -1457,7 +1466,8 @@ impl<'a> BatchBuilder<'a> { &info.top_right_inner_radius(), resource_cache, clip_buffers, - BasicRotationAngle::Clockwise90); + BasicRotationAngle::Clockwise90, + device_pixel_ratio); self.add_border_corner(clip, info.right.style, @@ -1472,7 +1482,8 @@ impl<'a> BatchBuilder<'a> { &info.bottom_right_inner_radius(), resource_cache, clip_buffers, - BasicRotationAngle::Clockwise180); + BasicRotationAngle::Clockwise180, + device_pixel_ratio); self.add_border_corner(clip, info.bottom.style, @@ -1487,7 +1498,8 @@ impl<'a> BatchBuilder<'a> { &info.bottom_left_inner_radius(), resource_cache, clip_buffers, - BasicRotationAngle::Clockwise270); + BasicRotationAngle::Clockwise270, + device_pixel_ratio); } // FIXME(pcwalton): Assumes rectangles are well-formed with origin in TL diff --git a/src/node_compiler.rs b/src/node_compiler.rs index 1c90813f57..3da92f81a5 100644 --- a/src/node_compiler.rs +++ b/src/node_compiler.rs @@ -125,7 +125,8 @@ impl NodeCompiler for AABBTreeNode { &clip, info, resource_cache, - &mut clip_buffers); + &mut clip_buffers, + device_pixel_ratio); } } } diff --git a/src/renderer.rs b/src/renderer.rs index 4d667b6c9a..29f25070cc 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -550,21 +550,12 @@ impl Renderer { inverted) => { let x = x as f32; let y = y as f32; + let device_pixel_ratio = self.device_pixel_ratio; + let inner_rx = inner_rx.to_f32_px(); let inner_ry = inner_ry.to_f32_px(); let outer_rx = outer_rx.to_f32_px(); let outer_ry = outer_ry.to_f32_px(); - - let border_program_id = self.border_program_id; - let color = if inverted { - ColorF::new(0.0, 0.0, 0.0, 1.0) - } else { - ColorF::new(1.0, 1.0, 1.0, 1.0) - }; - - let border_radii_outer = Point2D::new(outer_rx, outer_ry); - let border_radii_inner = Point2D::new(inner_rx, inner_ry); - let tessellated_rect = Rect::new(Point2D::new(0.0, 0.0), Size2D::new(outer_rx, outer_ry)); @@ -574,10 +565,35 @@ impl Renderer { tessellated_rect.tessellate_border_corner( &Size2D::new(outer_rx, outer_ry), &Size2D::new(inner_rx, inner_ry), + device_pixel_ratio, BasicRotationAngle::Upright, index) } }; + + // From here on out everything is in device coordinates. + let tessellated_rect = Rect::new( + Point2D::new(tessellated_rect.origin.x * device_pixel_ratio, + tessellated_rect.origin.y * device_pixel_ratio), + Size2D::new( + tessellated_rect.size.width * device_pixel_ratio, + tessellated_rect.size.height * device_pixel_ratio)); + + let inner_rx = inner_rx * device_pixel_ratio; + let inner_ry = inner_ry * device_pixel_ratio; + let outer_rx = outer_rx * device_pixel_ratio; + let outer_ry = outer_ry * device_pixel_ratio; + + let border_program_id = self.border_program_id; + let color = if inverted { + ColorF::new(0.0, 0.0, 0.0, 1.0) + } else { + ColorF::new(1.0, 1.0, 1.0, 1.0) + }; + + let border_radii_outer = Point2D::new(outer_rx, outer_ry); + let border_radii_inner = Point2D::new(inner_rx, inner_ry); + let border_position = Point2D::new(x - tessellated_rect.origin.x + outer_rx, y - tessellated_rect.origin.y + outer_ry); diff --git a/src/resource_cache.rs b/src/resource_cache.rs index 0b28c1b6cc..0c2750a96c 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -347,6 +347,10 @@ impl ResourceCache { context_id: &WebGLContextId) -> TextureId { self.webgl_textures.get(context_id).unwrap().clone() } + + pub fn device_pixel_ratio(&self) -> f32 { + self.device_pixel_ratio + } } fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, diff --git a/src/resource_list.rs b/src/resource_list.rs index 709abcdcc9..025fbdd057 100644 --- a/src/resource_list.rs +++ b/src/resource_list.rs @@ -191,7 +191,8 @@ impl BuildRequiredResources for AABBTreeNode { } SpecificDisplayItem::Border(ref info) => { for rect_index in 0..tessellator::quad_count_for_border_corner( - &info.radius.top_left) { + &info.radius.top_left, + resource_cache.device_pixel_ratio()) { resource_list.add_radius_raster(&info.radius.top_left, &info.top_left_inner_radius(), false, @@ -199,7 +200,8 @@ impl BuildRequiredResources for AABBTreeNode { ImageFormat::A8); } for rect_index in 0..tessellator::quad_count_for_border_corner( - &info.radius.top_right) { + &info.radius.top_right, + resource_cache.device_pixel_ratio()) { resource_list.add_radius_raster(&info.radius.top_right, &info.top_right_inner_radius(), false, @@ -207,7 +209,8 @@ impl BuildRequiredResources for AABBTreeNode { ImageFormat::A8); } for rect_index in 0..tessellator::quad_count_for_border_corner( - &info.radius.bottom_left) { + &info.radius.bottom_left, + resource_cache.device_pixel_ratio()) { resource_list.add_radius_raster(&info.radius.bottom_left, &info.bottom_left_inner_radius(), false, @@ -215,7 +218,8 @@ impl BuildRequiredResources for AABBTreeNode { ImageFormat::A8); } for rect_index in 0..tessellator::quad_count_for_border_corner( - &info.radius.bottom_right) { + &info.radius.bottom_right, + resource_cache.device_pixel_ratio()) { resource_list.add_radius_raster(&info.radius.bottom_right, &info.bottom_right_inner_radius(), false, diff --git a/src/tessellator.rs b/src/tessellator.rs index 6825d5022f..7d98fab793 100644 --- a/src/tessellator.rs +++ b/src/tessellator.rs @@ -3,8 +3,9 @@ use internal_types::BasicRotationAngle; const EPSILON: f32 = 0.01; -pub fn quad_count_for_border_corner(outer_radius: &Size2D) -> u32 { - if outer_radius.width < 32.0 && outer_radius.height < 32.0 { +pub fn quad_count_for_border_corner(outer_radius: &Size2D, device_pixel_ratio: f32) -> u32 { + let max = 32.0 / device_pixel_ratio; + if outer_radius.width < max && outer_radius.height < max { 1 } else { 4 @@ -15,6 +16,7 @@ pub trait BorderCornerTessellation { fn tessellate_border_corner(&self, outer_radius: &Size2D, inner_radius: &Size2D, + device_pixel_ratio: f32, rotation_angle: BasicRotationAngle, index: u32) -> Rect; @@ -24,10 +26,11 @@ impl BorderCornerTessellation for Rect { fn tessellate_border_corner(&self, outer_radius: &Size2D, inner_radius: &Size2D, + device_pixel_ratio: f32, rotation_angle: BasicRotationAngle, index: u32) -> Rect { - let quad_count = quad_count_for_border_corner(outer_radius); + let quad_count = quad_count_for_border_corner(outer_radius, device_pixel_ratio); if quad_count == 1 { return *self } diff --git a/src/texture_cache.rs b/src/texture_cache.rs index a9fc08399a..a7a66792de 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -757,9 +757,10 @@ impl TextureCache { device_pixel_ratio: f32) { let update_op = match item { &RasterItem::BorderRadius(ref op) => { - let rect = Rect::new(Point2D::new(0.0, 0.0), - Size2D::new(op.outer_radius_x.to_f32_px(), - op.outer_radius_y.to_f32_px())); + let rect = + Rect::new(Point2D::new(0.0, 0.0), + Size2D::new(op.outer_radius_x.to_f32_px(), + op.outer_radius_y.to_f32_px())); let tessellated_rect = match op.index { Some(index) => { rect.tessellate_border_corner( @@ -767,13 +768,15 @@ impl TextureCache { op.outer_radius_y.to_f32_px()), &Size2D::new(op.inner_radius_x.to_f32_px(), op.inner_radius_y.to_f32_px()), + device_pixel_ratio, BasicRotationAngle::Upright, index) } None => rect, }; - let width = tessellated_rect.size.width.round() as u32; - let height = tessellated_rect.size.height.round() as u32; + + let width = (tessellated_rect.size.width.round() * device_pixel_ratio) as u32; + let height = (tessellated_rect.size.height.round() * device_pixel_ratio) as u32; let allocation = self.allocate(image_id, 0,