From ca48941951cf661281449be5d6242e17bb23abf7 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Sat, 26 May 2018 11:09:20 -0400 Subject: [PATCH] Bug 1463416 - Update webrender to cset 63c71ca9bbe4dec0ebc9c9bc8ab65b06a6b40641. r=Gankro MozReview-Commit-ID: 21gmlBlV8En --HG-- extra : rebase_source : 89e3746c4254b9a20ca811be6419eb3901b4f319 --- gfx/webrender/examples/alpha_perf.rs | 2 +- gfx/webrender/examples/animation.rs | 2 +- gfx/webrender/examples/basic.rs | 4 +- gfx/webrender/examples/blob.rs | 8 +- gfx/webrender/examples/common/boilerplate.rs | 12 +- gfx/webrender/examples/document.rs | 2 +- gfx/webrender/examples/frame_output.rs | 8 +- gfx/webrender/examples/iframe.rs | 2 +- gfx/webrender/examples/image_resize.rs | 9 +- gfx/webrender/examples/multiwindow.rs | 8 +- gfx/webrender/examples/scrolling.rs | 2 +- .../examples/texture_cache_stress.rs | 24 +- gfx/webrender/examples/yuv.rs | 10 +- gfx/webrender/res/brush_image.glsl | 2 +- gfx/webrender/res/clip_shared.glsl | 3 +- gfx/webrender/res/cs_border_segment.glsl | 127 ++++ gfx/webrender/res/prim_shared.glsl | 25 +- gfx/webrender/res/ps_text_run.glsl | 48 +- gfx/webrender/res/snap.glsl | 11 +- gfx/webrender/src/batch.rs | 37 +- gfx/webrender/src/border.rs | 628 ++++++++++++++++-- gfx/webrender/src/display_list_flattener.rs | 31 +- gfx/webrender/src/glyph_cache.rs | 3 +- gfx/webrender/src/glyph_rasterizer/mod.rs | 256 +++++-- .../src/glyph_rasterizer/no_pathfinder.rs | 7 +- .../src/glyph_rasterizer/pathfinder.rs | 14 +- gfx/webrender/src/gpu_types.rs | 32 +- gfx/webrender/src/platform/macos/font.rs | 5 +- gfx/webrender/src/platform/unix/font.rs | 74 ++- gfx/webrender/src/platform/windows/font.rs | 8 +- gfx/webrender/src/prim_store.rs | 221 ++++-- gfx/webrender/src/render_backend.rs | 8 +- gfx/webrender/src/render_task.rs | 36 +- gfx/webrender/src/renderer.rs | 86 +++ gfx/webrender/src/resource_cache.rs | 47 +- gfx/webrender/src/scene_builder.rs | 6 +- gfx/webrender/src/shade.rs | 12 + gfx/webrender/src/tiling.rs | 24 +- gfx/webrender_api/Cargo.toml | 4 +- gfx/webrender_api/src/api.rs | 206 +++--- gfx/webrender_api/src/display_item.rs | 2 +- gfx/webrender_api/src/font.rs | 144 +--- gfx/webrender_api/src/image.rs | 10 +- gfx/webrender_api/src/units.rs | 2 + gfx/webrender_bindings/revision.txt | 2 +- gfx/wrench/src/json_frame_writer.rs | 4 +- gfx/wrench/src/rawtest.rs | 262 ++++++-- gfx/wrench/src/ron_frame_writer.rs | 4 +- gfx/wrench/src/wrench.rs | 39 +- gfx/wrench/src/yaml_frame_reader.rs | 15 +- gfx/wrench/src/yaml_frame_writer.rs | 7 +- 51 files changed, 1843 insertions(+), 702 deletions(-) create mode 100644 gfx/webrender/res/cs_border_segment.glsl diff --git a/gfx/webrender/examples/alpha_perf.rs b/gfx/webrender/examples/alpha_perf.rs index 9e65ab62a733..f0b5afb7b63a 100644 --- a/gfx/webrender/examples/alpha_perf.rs +++ b/gfx/webrender/examples/alpha_perf.rs @@ -23,7 +23,7 @@ impl Example for App { &mut self, _api: &RenderApi, builder: &mut DisplayListBuilder, - _resources: &mut ResourceUpdates, + _txn: &mut Transaction, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, diff --git a/gfx/webrender/examples/animation.rs b/gfx/webrender/examples/animation.rs index 6e5c3128ad9d..edfd82d53ace 100644 --- a/gfx/webrender/examples/animation.rs +++ b/gfx/webrender/examples/animation.rs @@ -34,7 +34,7 @@ impl Example for App { &mut self, _api: &RenderApi, builder: &mut DisplayListBuilder, - _resources: &mut ResourceUpdates, + _txn: &mut Transaction, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, diff --git a/gfx/webrender/examples/basic.rs b/gfx/webrender/examples/basic.rs index 01c70b01008b..d1c93432438b 100644 --- a/gfx/webrender/examples/basic.rs +++ b/gfx/webrender/examples/basic.rs @@ -182,7 +182,7 @@ impl Example for App { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - resources: &mut ResourceUpdates, + txn: &mut Transaction, _: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, @@ -201,7 +201,7 @@ impl Example for App { ); let image_mask_key = api.generate_image_key(); - resources.add_image( + txn.add_image( image_mask_key, ImageDescriptor::new(2, 2, ImageFormat::R8, true, false), ImageData::new(vec![0, 80, 180, 255]), diff --git a/gfx/webrender/examples/blob.rs b/gfx/webrender/examples/blob.rs index af2372b63d6a..95c490155950 100644 --- a/gfx/webrender/examples/blob.rs +++ b/gfx/webrender/examples/blob.rs @@ -16,7 +16,7 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender, channel}; -use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, ResourceUpdates}; +use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction}; // This example shows how to implement a very basic BlobImageRenderer that can only render // a checkerboard pattern. @@ -224,13 +224,13 @@ impl Example for App { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - resources: &mut ResourceUpdates, + txn: &mut Transaction, _framebuffer_size: api::DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, ) { let blob_img1 = api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img1, api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false), api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))), @@ -238,7 +238,7 @@ impl Example for App { ); let blob_img2 = api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img2, api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false), api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))), diff --git a/gfx/webrender/examples/common/boilerplate.rs b/gfx/webrender/examples/common/boilerplate.rs index 60bc91f6e9e6..4a86b8cecfc9 100644 --- a/gfx/webrender/examples/common/boilerplate.rs +++ b/gfx/webrender/examples/common/boilerplate.rs @@ -71,7 +71,7 @@ pub trait Example { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - resources: &mut ResourceUpdates, + txn: &mut Transaction, framebuffer_size: DeviceUintSize, pipeline_id: PipelineId, document_id: DocumentId, @@ -169,17 +169,16 @@ pub fn main_wrapper( let pipeline_id = PipelineId(0, 0); let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio); let mut builder = DisplayListBuilder::new(pipeline_id, layout_size); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); example.render( &api, &mut builder, - &mut resources, + &mut txn, framebuffer_size, pipeline_id, document_id, ); - let mut txn = Transaction::new(); txn.set_display_list( epoch, None, @@ -187,7 +186,6 @@ pub fn main_wrapper( builder.finalize(), true, ); - txn.update_resources(resources); txn.set_root_pipeline(pipeline_id); txn.generate_frame(); api.send_transaction(document_id, txn); @@ -251,12 +249,11 @@ pub fn main_wrapper( if custom_event { let mut builder = DisplayListBuilder::new(pipeline_id, layout_size); - let mut resources = ResourceUpdates::new(); example.render( &api, &mut builder, - &mut resources, + &mut txn, framebuffer_size, pipeline_id, document_id, @@ -268,7 +265,6 @@ pub fn main_wrapper( builder.finalize(), true, ); - txn.update_resources(resources); txn.generate_frame(); } api.send_transaction(document_id, txn); diff --git a/gfx/webrender/examples/document.rs b/gfx/webrender/examples/document.rs index 3329df08ca3d..f3d3d7bfe134 100644 --- a/gfx/webrender/examples/document.rs +++ b/gfx/webrender/examples/document.rs @@ -87,7 +87,7 @@ impl Example for App { &mut self, api: &RenderApi, base_builder: &mut DisplayListBuilder, - _: &mut ResourceUpdates, + _txn: &mut Transaction, framebuffer_size: DeviceUintSize, _: PipelineId, _: DocumentId, diff --git a/gfx/webrender/examples/frame_output.rs b/gfx/webrender/examples/frame_output.rs index a3489a159cc6..66b8d5f153e2 100644 --- a/gfx/webrender/examples/frame_output.rs +++ b/gfx/webrender/examples/frame_output.rs @@ -67,8 +67,8 @@ impl App { ) { // Generate the external image key that will be used to render the output document to the root document. self.external_image_key = Some(api.generate_image_key()); - let mut resources = ResourceUpdates::new(); - resources.add_image( + let mut txn = Transaction::new(); + txn.add_image( self.external_image_key.unwrap(), ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false), ImageData::External(ExternalImageData { @@ -112,10 +112,8 @@ impl App { builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0)); builder.pop_stacking_context(); - let mut txn = Transaction::new(); txn.set_root_pipeline(pipeline_id); txn.enable_frame_output(document.pipeline_id, true); - txn.update_resources(resources); txn.set_display_list( Epoch(0), Some(document.color), @@ -134,7 +132,7 @@ impl Example for App { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - _resources: &mut ResourceUpdates, + _txn: &mut Transaction, framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, diff --git a/gfx/webrender/examples/iframe.rs b/gfx/webrender/examples/iframe.rs index b73e17de130a..78d71dad1e59 100644 --- a/gfx/webrender/examples/iframe.rs +++ b/gfx/webrender/examples/iframe.rs @@ -23,7 +23,7 @@ impl Example for App { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - _resources: &mut ResourceUpdates, + _txn: &mut Transaction, _framebuffer_size: DeviceUintSize, pipeline_id: PipelineId, document_id: DocumentId, diff --git a/gfx/webrender/examples/image_resize.rs b/gfx/webrender/examples/image_resize.rs index e41677dd09fd..76a1ad906dd8 100644 --- a/gfx/webrender/examples/image_resize.rs +++ b/gfx/webrender/examples/image_resize.rs @@ -23,13 +23,13 @@ impl Example for App { &mut self, _api: &RenderApi, builder: &mut DisplayListBuilder, - resources: &mut ResourceUpdates, + txn: &mut Transaction, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, ) { let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32); - resources.add_image( + txn.add_image( self.image_key, image_descriptor, image_data, @@ -99,15 +99,14 @@ impl Example for App { } } - let mut updates = ResourceUpdates::new(); - updates.update_image( + let mut txn = Transaction::new(); + txn.update_image( self.image_key, ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false), ImageData::new(image_data), None, ); let mut txn = Transaction::new(); - txn.update_resources(updates); txn.generate_frame(); api.send_transaction(document_id, txn); } diff --git a/gfx/webrender/examples/multiwindow.rs b/gfx/webrender/examples/multiwindow.rs index 93cafbe13f37..707d21b10173 100644 --- a/gfx/webrender/examples/multiwindow.rs +++ b/gfx/webrender/examples/multiwindow.rs @@ -102,17 +102,15 @@ impl Window { let epoch = Epoch(0); let pipeline_id = PipelineId(0, 0); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); let font_key = api.generate_font_key(); let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf"); - resources.add_raw_font(font_key, font_bytes, 0); + txn.add_raw_font(font_key, font_bytes, 0); let font_instance_key = api.generate_font_instance_key(); - resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new()); + txn.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new()); - let mut txn = Transaction::new(); - txn.update_resources(resources); api.send_transaction(document_id, txn); Window { diff --git a/gfx/webrender/examples/scrolling.rs b/gfx/webrender/examples/scrolling.rs index e605c9940007..ea1ba7c54241 100644 --- a/gfx/webrender/examples/scrolling.rs +++ b/gfx/webrender/examples/scrolling.rs @@ -23,7 +23,7 @@ impl Example for App { &mut self, _api: &RenderApi, builder: &mut DisplayListBuilder, - _resources: &mut ResourceUpdates, + _txn: &mut Transaction, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, diff --git a/gfx/webrender/examples/texture_cache_stress.rs b/gfx/webrender/examples/texture_cache_stress.rs index 13a7bb1bd090..4f4270cd8e10 100644 --- a/gfx/webrender/examples/texture_cache_stress.rs +++ b/gfx/webrender/examples/texture_cache_stress.rs @@ -83,7 +83,7 @@ impl Example for App { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - resources: &mut ResourceUpdates, + txn: &mut Transaction, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, @@ -110,7 +110,7 @@ impl Example for App { let key1 = api.generate_image_key(); self.image_generator.generate_image(128); - resources.add_image( + txn.add_image( key0, ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false), ImageData::new(self.image_generator.take()), @@ -118,7 +118,7 @@ impl Example for App { ); self.image_generator.generate_image(128); - resources.add_image( + txn.add_image( key1, ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false), ImageData::new(self.image_generator.take()), @@ -200,7 +200,7 @@ impl Example for App { }, .. } => { - let mut updates = ResourceUpdates::new(); + let mut txn = Transaction::new(); match key { glutin::VirtualKeyCode::S => { @@ -214,7 +214,7 @@ impl Example for App { self.image_generator.generate_image(size); - updates.add_image( + txn.add_image( image_key, ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false), ImageData::new(self.image_generator.take()), @@ -226,13 +226,13 @@ impl Example for App { } } glutin::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() { - updates.delete_image(image_key); + txn.delete_image(image_key); }, glutin::VirtualKeyCode::U => if let Some(image_key) = self.image_key { let size = 128; self.image_generator.generate_image(size); - updates.update_image( + txn.update_image( image_key, ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false), ImageData::new(self.image_generator.take()), @@ -241,7 +241,7 @@ impl Example for App { }, glutin::VirtualKeyCode::E => { if let Some(image_key) = self.image_key.take() { - updates.delete_image(image_key); + txn.delete_image(image_key); } let size = 32; @@ -253,7 +253,7 @@ impl Example for App { image_type: ExternalImageType::Buffer, }; - updates.add_image( + txn.add_image( image_key, ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false), ImageData::External(image_data), @@ -264,14 +264,14 @@ impl Example for App { } glutin::VirtualKeyCode::R => { if let Some(image_key) = self.image_key.take() { - updates.delete_image(image_key); + txn.delete_image(image_key); } let image_key = api.generate_image_key(); let size = 32; self.image_generator.generate_image(size); - updates.add_image( + txn.add_image( image_key, ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false), ImageData::new(self.image_generator.take()), @@ -283,7 +283,7 @@ impl Example for App { _ => {} } - api.update_resources(updates); + api.update_resources(txn.resource_updates); return true; } _ => {} diff --git a/gfx/webrender/examples/yuv.rs b/gfx/webrender/examples/yuv.rs index 7c093766ea36..7acaaef2048f 100644 --- a/gfx/webrender/examples/yuv.rs +++ b/gfx/webrender/examples/yuv.rs @@ -78,7 +78,7 @@ impl Example for App { &mut self, api: &RenderApi, builder: &mut DisplayListBuilder, - resources: &mut ResourceUpdates, + txn: &mut Transaction, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, @@ -100,7 +100,7 @@ impl Example for App { let yuv_chanel2 = api.generate_image_key(); let yuv_chanel2_1 = api.generate_image_key(); let yuv_chanel3 = api.generate_image_key(); - resources.add_image( + txn.add_image( yuv_chanel1, ImageDescriptor::new(100, 100, ImageFormat::R8, true, false), ImageData::External(ExternalImageData { @@ -112,7 +112,7 @@ impl Example for App { }), None, ); - resources.add_image( + txn.add_image( yuv_chanel2, ImageDescriptor::new(100, 100, ImageFormat::RG8, true, false), ImageData::External(ExternalImageData { @@ -124,7 +124,7 @@ impl Example for App { }), None, ); - resources.add_image( + txn.add_image( yuv_chanel2_1, ImageDescriptor::new(100, 100, ImageFormat::R8, true, false), ImageData::External(ExternalImageData { @@ -136,7 +136,7 @@ impl Example for App { }), None, ); - resources.add_image( + txn.add_image( yuv_chanel3, ImageDescriptor::new(100, 100, ImageFormat::R8, true, false), ImageData::External(ExternalImageData { diff --git a/gfx/webrender/res/brush_image.glsl b/gfx/webrender/res/brush_image.glsl index c512f036598f..34cf4fdb60b9 100644 --- a/gfx/webrender/res/brush_image.glsl +++ b/gfx/webrender/res/brush_image.glsl @@ -48,7 +48,7 @@ vec2 transform_point_snapped( RectWithSize local_rect, mat4 transform ) { - vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect); + vec2 snap_offset = compute_snap_offset(local_pos, transform, local_rect, vec2(0.5)); vec4 world_pos = transform * vec4(local_pos, 0.0, 1.0); vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; diff --git a/gfx/webrender/res/clip_shared.glsl b/gfx/webrender/res/clip_shared.glsl index f0155734bd66..351946e2bd65 100644 --- a/gfx/webrender/res/clip_shared.glsl +++ b/gfx/webrender/res/clip_shared.glsl @@ -67,7 +67,8 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, scroll_node.transform, local_clip_rect, RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy), - snap_positions + snap_positions, + vec2(0.5) ); actual_pos -= snap_offsets; diff --git a/gfx/webrender/res/cs_border_segment.glsl b/gfx/webrender/res/cs_border_segment.glsl new file mode 100644 index 000000000000..0c195db34ddb --- /dev/null +++ b/gfx/webrender/res/cs_border_segment.glsl @@ -0,0 +1,127 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include shared,prim_shared,ellipse,shared_border + +flat varying vec4 vColor0; +flat varying vec4 vColor1; +flat varying vec4 vColorLine; +flat varying int vFeatures; +flat varying vec2 vClipCenter; +flat varying vec4 vClipRadii; +flat varying vec2 vClipSign; + +varying vec2 vPos; + +#define CLIP_RADII 1 +#define MIX_COLOR 2 + +#ifdef WR_VERTEX_SHADER + +in vec2 aTaskOrigin; +in vec4 aRect; +in vec4 aColor0; +in vec4 aColor1; +in int aFlags; +in vec2 aWidths; +in vec2 aRadii; + +#define SEGMENT_TOP_LEFT 0 +#define SEGMENT_TOP_RIGHT 1 +#define SEGMENT_BOTTOM_RIGHT 2 +#define SEGMENT_BOTTOM_LEFT 3 +#define SEGMENT_LEFT 4 +#define SEGMENT_TOP 5 +#define SEGMENT_RIGHT 6 +#define SEGMENT_BOTTOM 7 + +vec2 get_outer_corner_scale(int segment) { + vec2 p; + + switch (segment) { + case SEGMENT_TOP_LEFT: + p = vec2(0.0, 0.0); + break; + case SEGMENT_TOP_RIGHT: + p = vec2(1.0, 0.0); + break; + case SEGMENT_BOTTOM_RIGHT: + p = vec2(1.0, 1.0); + break; + case SEGMENT_BOTTOM_LEFT: + p = vec2(0.0, 1.0); + break; + default: + // Should never get hit + p = vec2(0.0); + break; + } + + return p; +} + +void main(void) { + vec2 pos = aRect.xy + aRect.zw * aPosition.xy; + + int segment = aFlags & 0xff; + int style = (aFlags >> 8) & 0xff; + + vec2 outer_scale = get_outer_corner_scale(segment); + vec2 outer = aRect.xy + outer_scale * aRect.zw; + vec2 clip_sign = 1.0 - 2.0 * outer_scale; + + vColor0 = aColor0; + vColor1 = aColor1; + vPos = pos; + + vFeatures = 0; + vClipSign = clip_sign; + vClipCenter = outer + clip_sign * aRadii; + vClipRadii = vec4(aRadii, aRadii - aWidths); + vColorLine = vec4(0.0); + + switch (segment) { + case SEGMENT_TOP_LEFT: + case SEGMENT_TOP_RIGHT: + case SEGMENT_BOTTOM_RIGHT: + case SEGMENT_BOTTOM_LEFT: + vFeatures |= (CLIP_RADII | MIX_COLOR); + vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x); + break; + default: + break; + } + + gl_Position = uTransform * vec4(aTaskOrigin + pos, 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + float aa_range = compute_aa_range(vPos); + float d = -1.0; + + // Apply color mix + float mix_factor = 0.0; + if ((vFeatures & MIX_COLOR) != 0) { + float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos); + mix_factor = distance_aa(aa_range, -d_line); + } + + // Apply clip radii + if ((vFeatures & CLIP_RADII) != 0) { + vec2 p = vPos - vClipCenter; + if (vClipSign.x * p.x < 0.0 && vClipSign.y * p.y < 0.0) { + float d_radii_a = distance_to_ellipse(p, vClipRadii.xy, aa_range); + float d_radii_b = distance_to_ellipse(p, vClipRadii.zw, aa_range); + float d_radii = max(d_radii_a, -d_radii_b); + d = max(d, d_radii); + } + } + + float alpha = distance_aa(aa_range, d); + vec4 color = mix(vColor0, vColor1, mix_factor); + oFragColor = color * alpha; +} +#endif diff --git a/gfx/webrender/res/prim_shared.glsl b/gfx/webrender/res/prim_shared.glsl index 8903ebfc3022..d7b9b33ac3bb 100644 --- a/gfx/webrender/res/prim_shared.glsl +++ b/gfx/webrender/res/prim_shared.glsl @@ -10,6 +10,7 @@ #define SUBPX_DIR_NONE 0 #define SUBPX_DIR_HORIZONTAL 1 #define SUBPX_DIR_VERTICAL 2 +#define SUBPX_DIR_MIXED 3 #define RASTER_LOCAL 0 #define RASTER_SCREEN 1 @@ -68,8 +69,7 @@ struct Glyph { }; Glyph fetch_glyph(int specific_prim_address, - int glyph_index, - int subpx_dir) { + int glyph_index) { // Two glyphs are packed in each texel in the GPU cache. int glyph_address = specific_prim_address + VECS_PER_TEXT_RUN + @@ -80,24 +80,6 @@ Glyph fetch_glyph(int specific_prim_address, // bug with equality comparisons on integers. vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 != 0)); - // In subpixel mode, the subpixel offset has already been - // accounted for while rasterizing the glyph. - switch (subpx_dir) { - case SUBPX_DIR_NONE: - break; - case SUBPX_DIR_HORIZONTAL: - // Glyphs positioned [-0.125, 0.125] get a - // subpx position of zero. So include that - // offset in the glyph position to ensure - // we round to the correct whole position. - glyph.x = floor(glyph.x + 0.125); - break; - case SUBPX_DIR_VERTICAL: - glyph.y = floor(glyph.y + 0.125); - break; - default: break; - } - return Glyph(glyph); } @@ -231,7 +213,8 @@ VertexInfo write_vertex(RectWithSize instance_rect, vec2 snap_offset = compute_snap_offset( clamped_local_pos, scroll_node.transform, - snap_rect + snap_rect, + vec2(0.5) ); // Transform the current vertex to world space. diff --git a/gfx/webrender/res/ps_text_run.glsl b/gfx/webrender/res/ps_text_run.glsl index 471db4bd0056..acc21e80e49a 100644 --- a/gfx/webrender/res/ps_text_run.glsl +++ b/gfx/webrender/res/ps_text_run.glsl @@ -20,7 +20,14 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, float z, ClipScrollNode scroll_node, PictureTask task, - RectWithSize snap_rect) { + RectWithSize snap_rect, + vec2 snap_bias) { +#if defined(WR_FEATURE_GLYPH_TRANSFORM) || !defined(WR_FEATURE_TRANSFORM) + // Ensure the transform does not contain a subpixel translation to ensure + // that glyph snapping is stable for equivalent glyph subpixel positions. + scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5); +#endif + // Transform the current vertex to world space. vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0); @@ -34,15 +41,14 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, #ifdef WR_FEATURE_GLYPH_TRANSFORM // For transformed subpixels, we just need to align the glyph origin to a device pixel. - // Only check the scroll node transform's translation since the scales and axes match. - vec2 world_snap_p0 = snap_rect.p0 + scroll_node.transform[3].xy * uDevicePixelRatio; - final_pos += floor(world_snap_p0 + 0.5) - world_snap_p0; + final_pos += floor(snap_rect.p0 + snap_bias) - snap_rect.p0; #elif !defined(WR_FEATURE_TRANSFORM) // Compute the snapping offset only if the scroll node transform is axis-aligned. final_pos += compute_snap_offset( clamped_local_pos, scroll_node.transform, - snap_rect + snap_rect, + snap_bias ); #endif @@ -71,9 +77,7 @@ void main(void) { color_mode = uMode; } - Glyph glyph = fetch_glyph(prim.specific_prim_address, - glyph_index, - subpx_dir); + Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index); GlyphResource res = fetch_glyph_resource(resource_address); #ifdef WR_FEATURE_GLYPH_TRANSFORM @@ -112,12 +116,38 @@ void main(void) { local_pos = clamp_rect(local_pos, prim.local_clip_rect); #endif + vec2 snap_bias; + // In subpixel mode, the subpixel offset has already been + // accounted for while rasterizing the glyph. However, we + // must still round with a subpixel bias rather than rounding + // to the nearest whole pixel, depending on subpixel direciton. + switch (subpx_dir) { + case SUBPX_DIR_NONE: + default: + snap_bias = vec2(0.5); + break; + case SUBPX_DIR_HORIZONTAL: + // Glyphs positioned [-0.125, 0.125] get a + // subpx position of zero. So include that + // offset in the glyph position to ensure + // we round to the correct whole position. + snap_bias = vec2(0.125, 0.5); + break; + case SUBPX_DIR_VERTICAL: + snap_bias = vec2(0.5, 0.125); + break; + case SUBPX_DIR_MIXED: + snap_bias = vec2(0.125); + break; + } + VertexInfo vi = write_text_vertex(local_pos, prim.local_clip_rect, prim.z, prim.scroll_node, prim.task, - glyph_rect); + glyph_rect, + snap_bias); #ifdef WR_FEATURE_GLYPH_TRANSFORM vec2 f = (transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size; diff --git a/gfx/webrender/res/snap.glsl b/gfx/webrender/res/snap.glsl index 92eb15d3edce..10661319024d 100644 --- a/gfx/webrender/res/snap.glsl +++ b/gfx/webrender/res/snap.glsl @@ -27,10 +27,11 @@ vec2 compute_snap_offset_impl( mat4 transform, RectWithSize snap_rect, RectWithSize reference_rect, - vec4 snap_positions) { + vec4 snap_positions, + vec2 snap_bias) { /// World offsets applied to the corners of the snap rectangle. - vec4 snap_offsets = floor(snap_positions + 0.5) - snap_positions; + vec4 snap_offsets = floor(snap_positions + snap_bias.xyxy) - snap_positions; /// Compute the position of this vertex inside the snap rectangle. vec2 normalized_snap_pos = (reference_pos - reference_rect.p0) / reference_rect.size; @@ -43,7 +44,8 @@ vec2 compute_snap_offset_impl( // given local position on the scroll_node and a snap rectangle. vec2 compute_snap_offset(vec2 local_pos, mat4 transform, - RectWithSize snap_rect) { + RectWithSize snap_rect, + vec2 snap_bias) { vec4 snap_positions = compute_snap_positions( transform, snap_rect @@ -54,7 +56,8 @@ vec2 compute_snap_offset(vec2 local_pos, transform, snap_rect, snap_rect, - snap_positions + snap_positions, + snap_bias ); return snap_offsets; diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index 69242bb4c06f..941bcebeae09 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -4,7 +4,7 @@ use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize}; use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering, LayoutRect}; -use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat}; +use api::{DeviceIntPoint, YuvColorSpace, YuvFormat}; use api::{LayoutToWorldTransform, WorldPixel}; use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind}; use clip::{ClipSource, ClipStore, ClipWorkItem}; @@ -22,7 +22,7 @@ use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, CachedGradient, DeferredResolve}; use prim_store::{EdgeAaSegmentMask, ImageSource, PictureIndex, PrimitiveIndex, PrimitiveKind}; use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore, VisibleGradientTile}; -use prim_store::CachedGradientIndex; +use prim_store::{BorderSource, CachedGradientIndex}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree}; use renderer::{BlendMode, ImageBufferKind}; use renderer::{BLOCKS_PER_UV_RECT, ShaderColorMode}; @@ -1153,6 +1153,7 @@ impl AlphaBatchBuilder { ctx.device_pixel_scale, Some(scroll_node.transform), ); + let subpx_dir = font.get_subpx_dir(); let glyph_fetch_buffer = &mut self.glyph_fetch_buffer; let batch_list = &mut self.batch_list; @@ -1170,11 +1171,7 @@ impl AlphaBatchBuilder { glyph_format = glyph_format.ignore_color(); } - let subpx_dir = match glyph_format { - GlyphFormat::Bitmap | - GlyphFormat::ColorBitmap => SubpixelDirection::None, - _ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode), - }; + let subpx_dir = subpx_dir.limit_by(glyph_format); let textures = BatchTextures { colors: [ @@ -1524,13 +1521,25 @@ impl BrushPrimitive { )) } } - BrushKind::Border { request, .. } => { - let cache_item = resolve_image( - request, - resource_cache, - gpu_cache, - deferred_resolves, - ); + BrushKind::Border { ref source, .. } => { + let cache_item = match *source { + BorderSource::Image(request) => { + resolve_image( + request, + resource_cache, + gpu_cache, + deferred_resolves, + ) + } + BorderSource::Border { ref handle, .. } => { + let rt_handle = handle + .as_ref() + .expect("bug: render task handle not allocated"); + let rt_cache_entry = resource_cache + .get_cached_render_task(rt_handle); + resource_cache.get_texture_cache_item(&rt_cache_entry.handle) + } + }; if cache_item.texture_id == SourceTexture::Invalid { None diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs index a79068a6d5c6..ea7f47b1ee2a 100644 --- a/gfx/webrender/src/border.rs +++ b/gfx/webrender/src/border.rs @@ -2,16 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipMode, ColorF, LayoutPoint}; -use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, NormalBorder}; +use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayoutPoint}; +use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale}; +use api::{DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder}; +use app_units::Au; use clip::ClipSource; use ellipse::Ellipse; use display_list_flattener::DisplayListFlattener; -use gpu_types::BrushFlags; +use gpu_types::{BorderInstance, BorderSegment, BrushFlags}; use gpu_cache::GpuDataRequest; -use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor}; -use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain}; -use util::{lerp, pack_as_float}; +use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentDescriptor}; +use prim_store::{BorderSource, EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain}; +use util::{lerp, pack_as_float, RectHelpers}; #[repr(u8)] #[derive(Debug, Copy, Clone, PartialEq)] @@ -36,6 +38,96 @@ enum BorderCorner { BottomRight, } +trait AuSizeConverter { + fn to_au(&self) -> LayoutSizeAu; +} + +impl AuSizeConverter for LayoutSize { + fn to_au(&self) -> LayoutSizeAu { + LayoutSizeAu::new( + Au::from_f32_px(self.width), + Au::from_f32_px(self.height), + ) + } +} + +// TODO(gw): Perhaps there is a better way to store +// the border cache key than duplicating +// all the border structs with hashable +// variants... + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderRadiusAu { + pub top_left: LayoutSizeAu, + pub top_right: LayoutSizeAu, + pub bottom_left: LayoutSizeAu, + pub bottom_right: LayoutSizeAu, +} + +impl From for BorderRadiusAu { + fn from(radius: BorderRadius) -> BorderRadiusAu { + BorderRadiusAu { + top_left: radius.top_left.to_au(), + top_right: radius.top_right.to_au(), + bottom_right: radius.bottom_right.to_au(), + bottom_left: radius.bottom_left.to_au(), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderWidthsAu { + pub left: Au, + pub top: Au, + pub right: Au, + pub bottom: Au, +} + +impl From for BorderWidthsAu { + fn from(widths: BorderWidths) -> Self { + BorderWidthsAu { + left: Au::from_f32_px(widths.left), + top: Au::from_f32_px(widths.top), + right: Au::from_f32_px(widths.right), + bottom: Au::from_f32_px(widths.bottom), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderSideAu { + pub color: ColorU, + pub style: BorderStyle, +} + +impl From for BorderSideAu { + fn from(side: BorderSide) -> Self { + BorderSideAu { + color: side.color.into(), + style: side.style, + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderCacheKey { + pub left: BorderSideAu, + pub right: BorderSideAu, + pub top: BorderSideAu, + pub bottom: BorderSideAu, + pub radius: BorderRadiusAu, + pub widths: BorderWidthsAu, + pub scale: Au, +} + #[derive(Clone, Debug, PartialEq)] pub enum BorderCornerKind { None, @@ -374,60 +466,54 @@ impl<'a> DisplayListFlattener<'a> { let top = &border.top; let bottom = &border.bottom; - let constant_color = left.color; - let is_simple_border = [left, top, right, bottom].iter().all(|edge| { - edge.style == BorderStyle::Solid && - edge.color == constant_color + let brush_border_supported = [left, top, right, bottom].iter().all(|edge| { + match edge.style { + BorderStyle::Solid | + BorderStyle::Hidden | + BorderStyle::None | + BorderStyle::Inset | + BorderStyle::Outset => { + true + } + + BorderStyle::Double | + BorderStyle::Dotted | + BorderStyle::Dashed | + BorderStyle::Groove | + BorderStyle::Ridge => { + false + } + } }); - if is_simple_border { - let extra_clips = vec![ - ClipSource::new_rounded_rect( - info.rect, - border.radius, - ClipMode::Clip, - ), - ClipSource::new_rounded_rect( - LayoutRect::new( - LayoutPoint::new( - info.rect.origin.x + widths.left, - info.rect.origin.y + widths.top, - ), - LayoutSize::new( - info.rect.size.width - widths.left - widths.right, - info.rect.size.height - widths.top - widths.bottom, - ), - ), - BorderRadius { - top_left: LayoutSize::new( - (border.radius.top_left.width - widths.left).max(0.0), - (border.radius.top_left.height - widths.top).max(0.0), - ), - top_right: LayoutSize::new( - (border.radius.top_right.width - widths.right).max(0.0), - (border.radius.top_right.height - widths.top).max(0.0), - ), - bottom_left: LayoutSize::new( - (border.radius.bottom_left.width - widths.left).max(0.0), - (border.radius.bottom_left.height - widths.bottom).max(0.0), - ), - bottom_right: LayoutSize::new( - (border.radius.bottom_right.width - widths.right).max(0.0), - (border.radius.bottom_right.height - widths.bottom).max(0.0), - ), + if brush_border_supported { + let prim = BrushPrimitive::new( + BrushKind::Border { + source: BorderSource::Border { + border, + widths: *widths, + cache_key: BorderCacheKey { + left: border.left.into(), + top: border.top.into(), + right: border.right.into(), + bottom: border.bottom.into(), + widths: (*widths).into(), + radius: border.radius.into(), + scale: Au::from_f32_px(0.0), + }, + task_info: None, + handle: None, }, - ClipMode::ClipOut, - ), - ]; - - self.add_solid_rectangle( - clip_and_scroll, - info, - border.top.color, + }, None, - extra_clips, ); + self.add_primitive( + clip_and_scroll, + info, + Vec::new(), + PrimitiveContainer::Brush(prim), + ); return; } @@ -505,8 +591,7 @@ impl<'a> DisplayListFlattener<'a> { let p3 = info.rect.bottom_right(); let segment = |x0, y0, x1, y1| BrushSegment::new( - LayoutPoint::new(x0, y0), - LayoutSize::new(x1-x0, y1-y0), + LayoutRect::from_floats(x0, y0, x1, y1), true, EdgeAaSegmentMask::all(), // Note: this doesn't seem right, needs revision [0.0; 4], @@ -929,3 +1014,434 @@ impl DotInfo { DotInfo { arc_pos, diameter } } } + +#[derive(Debug)] +pub struct BorderRenderTaskInfo { + pub instances: Vec, + pub segments: Vec, + pub size: DeviceIntSize, +} + +impl BorderRenderTaskInfo { + pub fn new( + rect: &LayoutRect, + border: &NormalBorder, + widths: &BorderWidths, + scale: LayoutToDeviceScale, + ) -> Self { + let mut instances = Vec::new(); + let mut segments = Vec::new(); + + let dp_width_top = (widths.top * scale.0).ceil(); + let dp_width_bottom = (widths.bottom * scale.0).ceil(); + let dp_width_left = (widths.left * scale.0).ceil(); + let dp_width_right = (widths.right * scale.0).ceil(); + + let dp_corner_tl = (border.radius.top_left * scale).ceil(); + let dp_corner_tr = (border.radius.top_right * scale).ceil(); + let dp_corner_bl = (border.radius.bottom_left * scale).ceil(); + let dp_corner_br = (border.radius.bottom_right * scale).ceil(); + + let dp_size_tl = DeviceSize::new( + dp_corner_tl.width.max(dp_width_left), + dp_corner_tl.height.max(dp_width_top), + ); + let dp_size_tr = DeviceSize::new( + dp_corner_tr.width.max(dp_width_right), + dp_corner_tr.height.max(dp_width_top), + ); + let dp_size_br = DeviceSize::new( + dp_corner_br.width.max(dp_width_right), + dp_corner_br.height.max(dp_width_bottom), + ); + let dp_size_bl = DeviceSize::new( + dp_corner_bl.width.max(dp_width_left), + dp_corner_bl.height.max(dp_width_bottom), + ); + + let local_size_tl = LayoutSize::new( + border.radius.top_left.width.max(widths.left), + border.radius.top_left.height.max(widths.top), + ); + let local_size_tr = LayoutSize::new( + border.radius.top_right.width.max(widths.right), + border.radius.top_right.height.max(widths.top), + ); + let local_size_br = LayoutSize::new( + border.radius.bottom_right.width.max(widths.right), + border.radius.bottom_right.height.max(widths.bottom), + ); + let local_size_bl = LayoutSize::new( + border.radius.bottom_left.width.max(widths.left), + border.radius.bottom_left.height.max(widths.bottom), + ); + + // TODO(gw): The inner and outer widths don't matter for simple + // border types. Once we push dashing and dotted styles + // through border brushes, we need to calculate an + // appropriate length here. + let width_inner = 16.0; + let height_inner = 16.0; + + let size = DeviceSize::new( + dp_size_tl.width.max(dp_size_bl.width) + width_inner + dp_size_tr.width.max(dp_size_br.width), + dp_size_tl.height.max(dp_size_tr.height) + height_inner + dp_size_bl.height.max(dp_size_br.height), + ); + + // These modulate colors are not part of the specification. They + // are derived from the Gecko source code and experimentation, and + // used to modulate the colors in order to generate colors for + // the inset/outset and groove/ridge border styles. + let left_color = border.left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7); + let top_color = border.top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7); + let right_color = border.right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3); + let bottom_color = border.bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3); + + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x, + rect.origin.y + local_size_tl.height, + rect.origin.x + widths.left, + rect.origin.y + rect.size.height - local_size_bl.height, + ), + DeviceRect::from_floats( + 0.0, + dp_size_tl.height, + dp_width_left, + size.height - dp_size_bl.height, + ), + border.left.style, + left_color, + BorderSegment::Left, + EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT, + &mut instances, + BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y, + &mut segments, + ); + + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x + local_size_tl.width, + rect.origin.y, + rect.origin.x + rect.size.width - local_size_tr.width, + rect.origin.y + widths.top, + ), + DeviceRect::from_floats( + dp_size_tl.width, + 0.0, + size.width - dp_size_tr.width, + dp_width_top, + ), + border.top.style, + top_color, + BorderSegment::Top, + EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM, + &mut instances, + BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X, + &mut segments, + ); + + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x + rect.size.width - widths.right, + rect.origin.y + local_size_tr.height, + rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height - local_size_br.height, + ), + DeviceRect::from_floats( + size.width - dp_width_right, + dp_size_tr.height, + size.width, + size.height - dp_size_br.height, + ), + border.right.style, + right_color, + BorderSegment::Right, + EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT, + &mut instances, + BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y, + &mut segments, + ); + + add_edge_segment( + LayoutRect::from_floats( + rect.origin.x + local_size_bl.width, + rect.origin.y + rect.size.height - widths.bottom, + rect.origin.x + rect.size.width - local_size_br.width, + rect.origin.y + rect.size.height, + ), + DeviceRect::from_floats( + dp_size_bl.width, + size.height - dp_width_bottom, + size.width - dp_size_br.width, + size.height, + ), + border.bottom.style, + bottom_color, + BorderSegment::Bottom, + EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP, + &mut instances, + BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X, + &mut segments, + ); + + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x, + rect.origin.y, + rect.origin.x + local_size_tl.width, + rect.origin.y + local_size_tl.height, + ), + DeviceRect::from_floats( + 0.0, + 0.0, + dp_size_tl.width, + dp_size_tl.height, + ), + border.left.style, + left_color, + border.top.style, + top_color, + DeviceSize::new(dp_width_left, dp_width_top), + dp_corner_tl, + BorderSegment::TopLeft, + EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT, + &mut instances, + &mut segments, + ); + + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x + rect.size.width - local_size_tr.width, + rect.origin.y, + rect.origin.x + rect.size.width, + rect.origin.y + local_size_tr.height, + ), + DeviceRect::from_floats( + size.width - dp_size_tr.width, + 0.0, + size.width, + dp_size_tr.height, + ), + border.top.style, + top_color, + border.right.style, + right_color, + DeviceSize::new(dp_width_right, dp_width_top), + dp_corner_tr, + BorderSegment::TopRight, + EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT, + &mut instances, + &mut segments, + ); + + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x + rect.size.width - local_size_br.width, + rect.origin.y + rect.size.height - local_size_br.height, + rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height, + ), + DeviceRect::from_floats( + size.width - dp_size_br.width, + size.height - dp_size_br.height, + size.width, + size.height, + ), + border.right.style, + right_color, + border.bottom.style, + bottom_color, + DeviceSize::new(dp_width_right, dp_width_bottom), + dp_corner_br, + BorderSegment::BottomRight, + EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT, + &mut instances, + &mut segments, + ); + + add_corner_segment( + LayoutRect::from_floats( + rect.origin.x, + rect.origin.y + rect.size.height - local_size_bl.height, + rect.origin.x + local_size_bl.width, + rect.origin.y + rect.size.height, + ), + DeviceRect::from_floats( + 0.0, + size.height - dp_size_bl.height, + dp_size_bl.width, + size.height, + ), + border.bottom.style, + bottom_color, + border.left.style, + left_color, + DeviceSize::new(dp_width_left, dp_width_bottom), + dp_corner_bl, + BorderSegment::BottomLeft, + EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT, + &mut instances, + &mut segments, + ); + + BorderRenderTaskInfo { + segments, + instances, + size: size.to_i32(), + } + } +} + +fn add_brush_segment( + image_rect: LayoutRect, + task_rect: DeviceRect, + brush_flags: BrushFlags, + edge_flags: EdgeAaSegmentMask, + brush_segments: &mut Vec, +) { + brush_segments.push( + BrushSegment::new( + image_rect, + true, + edge_flags, + [ + task_rect.origin.x, + task_rect.origin.y, + task_rect.origin.x + task_rect.size.width, + task_rect.origin.y + task_rect.size.height, + ], + brush_flags, + ) + ); +} + +fn add_segment( + task_rect: DeviceRect, + style0: BorderStyle, + style1: BorderStyle, + color0: ColorF, + color1: ColorF, + segment: BorderSegment, + instances: &mut Vec, + widths: DeviceSize, + radius: DeviceSize, +) { + let flags = (segment as i32) | + ((style0 as i32) << 8) | + ((style1 as i32) << 16); + + let base_instance = BorderInstance { + task_origin: DevicePoint::zero(), + local_rect: task_rect, + flags, + color0: color0.premultiplied(), + color1: color1.premultiplied(), + widths, + radius, + }; + + instances.push(base_instance); +} + +fn add_corner_segment( + image_rect: LayoutRect, + task_rect: DeviceRect, + mut style0: BorderStyle, + color0: ColorF, + mut style1: BorderStyle, + color1: ColorF, + widths: DeviceSize, + radius: DeviceSize, + segment: BorderSegment, + edge_flags: EdgeAaSegmentMask, + instances: &mut Vec, + brush_segments: &mut Vec, +) { + // TODO(gw): This will need to be a bit more involved when + // we support other border types here. For example, + // groove / ridge borders will always need to + // use two instances. + + if color0.a <= 0.0 && color1.a <= 0.0 { + return; + } + + if widths.width <= 0.0 && widths.height <= 0.0 { + return; + } + + let style0_hidden = style0 == BorderStyle::Hidden || style0 == BorderStyle::None; + let style1_hidden = style1 == BorderStyle::Hidden || style1 == BorderStyle::None; + + if style0_hidden && style1_hidden { + return; + } + + if style0_hidden { + style0 = style1; + } + if style1_hidden { + style1 = style0; + } + + add_segment( + task_rect, + style0, + style1, + color0, + color1, + segment, + instances, + widths, + radius, + ); + + add_brush_segment( + image_rect, + task_rect, + BrushFlags::SEGMENT_RELATIVE, + edge_flags, + brush_segments, + ); +} + +fn add_edge_segment( + image_rect: LayoutRect, + task_rect: DeviceRect, + style: BorderStyle, + color: ColorF, + segment: BorderSegment, + edge_flags: EdgeAaSegmentMask, + instances: &mut Vec, + brush_flags: BrushFlags, + brush_segments: &mut Vec, +) { + if color.a <= 0.0 { + return; + } + + if style == BorderStyle::Hidden || style == BorderStyle::None { + return; + } + + add_segment( + task_rect, + style, + style, + color, + color, + segment, + instances, + DeviceSize::zero(), + DeviceSize::zero(), + ); + + add_brush_segment( + image_rect, + task_rect, + brush_flags, + edge_flags, + brush_segments, + ); +} diff --git a/gfx/webrender/src/display_list_flattener.rs b/gfx/webrender/src/display_list_flattener.rs index 4ee572950bb3..3ab4d1ef29ce 100644 --- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -27,7 +27,7 @@ use internal_types::{FastHashMap, FastHashSet}; use picture::PictureCompositeMode; use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient}; use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageSource}; -use prim_store::{BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore}; +use prim_store::{BorderSource, BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore}; use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu}; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; @@ -1636,8 +1636,7 @@ impl<'a> DisplayListFlattener<'a> { } let segment = BrushSegment::new( - rect.origin, - rect.size, + rect, true, EdgeAaSegmentMask::empty(), [ @@ -1741,13 +1740,15 @@ impl<'a> DisplayListFlattener<'a> { let prim = PrimitiveContainer::Brush(match border.source { NinePatchBorderSource::Image(image_key) => { + let source = BorderSource::Image(ImageRequest { + key: image_key, + rendering: ImageRendering::Auto, + tile: None, + }); + BrushPrimitive::new( BrushKind::Border { - request: ImageRequest { - key: image_key, - rendering: ImageRendering::Auto, - tile: None, - }, + source }, Some(descriptor), ) @@ -1966,20 +1967,18 @@ impl<'a> DisplayListFlattener<'a> { *text_color, font_instance.bg_color, render_mode, - font_instance.subpx_dir, flags, font_instance.platform_options, font_instance.variations.clone(), ); - TextRunPrimitiveCpu { - font: prim_font, + TextRunPrimitiveCpu::new( + prim_font, + run_offset, glyph_range, - glyph_gpu_blocks: Vec::new(), - glyph_keys: Vec::new(), - offset: run_offset, - shadow: false, + Vec::new(), + false, glyph_raster_space, - } + ) }; self.add_primitive( diff --git a/gfx/webrender/src/glyph_cache.rs b/gfx/webrender/src/glyph_cache.rs index c1e96589d58e..6217722bfa43 100644 --- a/gfx/webrender/src/glyph_cache.rs +++ b/gfx/webrender/src/glyph_cache.rs @@ -4,8 +4,7 @@ #[cfg(feature = "pathfinder")] use api::DeviceIntPoint; -use api::GlyphKey; -use glyph_rasterizer::{FontInstance, GlyphFormat}; +use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey}; use internal_types::FastHashMap; use render_task::RenderTaskCache; #[cfg(feature = "pathfinder")] diff --git a/gfx/webrender/src/glyph_rasterizer/mod.rs b/gfx/webrender/src/glyph_rasterizer/mod.rs index bcba322724c9..1eddbcd2a806 100644 --- a/gfx/webrender/src/glyph_rasterizer/mod.rs +++ b/gfx/webrender/src/glyph_rasterizer/mod.rs @@ -2,11 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ColorF, ColorU}; +use api::{ColorF, ColorU, DevicePoint}; use api::{FontInstanceFlags, FontInstancePlatformOptions}; use api::{FontKey, FontRenderMode, FontTemplate, FontVariation}; -use api::{GlyphDimensions, GlyphKey, LayoutToWorldTransform, SubpixelDirection}; +use api::{GlyphIndex, GlyphDimensions}; +use api::{LayoutPoint, LayoutToWorldTransform, WorldPoint}; use app_units::Au; +use euclid::approxeq::ApproxEq; use internal_types::ResourceCacheError; use platform::font::FontContext; use rayon::ThreadPool; @@ -127,7 +129,27 @@ impl FontTransform { } pub fn flip_y(&self) -> Self { - FontTransform::new(self.scale_x, -self.skew_y, self.skew_y, -self.scale_y) + FontTransform::new(self.scale_x, -self.skew_x, self.skew_y, -self.scale_y) + } + + pub fn transform(&self, point: &LayoutPoint) -> WorldPoint { + WorldPoint::new( + self.scale_x * point.x + self.skew_x * point.y, + self.skew_y * point.x + self.scale_y * point.y, + ) + } + + pub fn get_subpx_dir(&self) -> SubpixelDirection { + if self.skew_y.approx_eq(&0.0) { + // The X axis is not projected onto the Y axis + SubpixelDirection::Horizontal + } else if self.scale_x.approx_eq(&0.0) { + // The X axis has been swapped with the Y axis + SubpixelDirection::Vertical + } else { + // Use subpixel precision on all axes + SubpixelDirection::Mixed + } } } @@ -151,7 +173,6 @@ pub struct FontInstance { pub color: ColorU, pub bg_color: ColorU, pub render_mode: FontRenderMode, - pub subpx_dir: SubpixelDirection, pub flags: FontInstanceFlags, pub platform_options: Option, pub variations: Vec, @@ -165,7 +186,6 @@ impl FontInstance { color: ColorF, bg_color: ColorU, render_mode: FontRenderMode, - subpx_dir: SubpixelDirection, flags: FontInstanceFlags, platform_options: Option, variations: Vec, @@ -180,7 +200,6 @@ impl FontInstance { color: color.into(), bg_color, render_mode, - subpx_dir, flags, platform_options, variations, @@ -196,12 +215,38 @@ impl FontInstance { if self.transform.is_identity() { GlyphFormat::Subpixel } else { GlyphFormat::TransformedSubpixel } } + pub fn disable_subpixel_aa(&mut self) { + self.render_mode = self.render_mode.limit_by(FontRenderMode::Alpha); + } + + pub fn disable_subpixel_position(&mut self) { + self.flags.remove(FontInstanceFlags::SUBPIXEL_POSITION); + } + + pub fn use_subpixel_position(&self) -> bool { + self.flags.contains(FontInstanceFlags::SUBPIXEL_POSITION) && + self.render_mode != FontRenderMode::Mono + } + + pub fn get_subpx_dir(&self) -> SubpixelDirection { + if self.use_subpixel_position() { + let mut subpx_dir = self.transform.get_subpx_dir(); + if self.flags.contains(FontInstanceFlags::TRANSPOSE) { + subpx_dir = subpx_dir.swap_xy(); + } + subpx_dir + } else { + SubpixelDirection::None + } + } + #[allow(dead_code)] pub fn get_subpx_offset(&self, glyph: &GlyphKey) -> (f64, f64) { - match self.subpx_dir { - SubpixelDirection::None => (0.0, 0.0), - SubpixelDirection::Horizontal => (glyph.subpixel_offset.into(), 0.0), - SubpixelDirection::Vertical => (0.0, glyph.subpixel_offset.into()), + if self.use_subpixel_position() { + let (dx, dy) = glyph.subpixel_offset; + (dx.into(), dy.into()) + } else { + (0.0, 0.0) } } @@ -227,6 +272,111 @@ impl FontInstance { } } +#[repr(u32)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] +pub enum SubpixelDirection { + None = 0, + Horizontal, + Vertical, + Mixed, +} + +impl SubpixelDirection { + // Limit the subpixel direction to what is supported by the glyph format. + pub fn limit_by(self, glyph_format: GlyphFormat) -> Self { + match glyph_format { + GlyphFormat::Bitmap | + GlyphFormat::ColorBitmap => SubpixelDirection::None, + _ => self, + } + } + + pub fn swap_xy(self) -> Self { + match self { + SubpixelDirection::None | SubpixelDirection::Mixed => self, + SubpixelDirection::Horizontal => SubpixelDirection::Vertical, + SubpixelDirection::Vertical => SubpixelDirection::Horizontal, + } + } +} + +#[repr(u8)] +#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum SubpixelOffset { + Zero = 0, + Quarter = 1, + Half = 2, + ThreeQuarters = 3, +} + +impl SubpixelOffset { + // Skia quantizes subpixel offsets into 1/4 increments. + // Given the absolute position, return the quantized increment + fn quantize(pos: f32) -> Self { + // Following the conventions of Gecko and Skia, we want + // to quantize the subpixel position, such that abs(pos) gives: + // [0.0, 0.125) -> Zero + // [0.125, 0.375) -> Quarter + // [0.375, 0.625) -> Half + // [0.625, 0.875) -> ThreeQuarters, + // [0.875, 1.0) -> Zero + // The unit tests below check for this. + let apos = ((pos - pos.floor()) * 8.0) as i32; + + match apos { + 0 | 7 => SubpixelOffset::Zero, + 1...2 => SubpixelOffset::Quarter, + 3...4 => SubpixelOffset::Half, + 5...6 => SubpixelOffset::ThreeQuarters, + _ => unreachable!("bug: unexpected quantized result"), + } + } +} + +impl Into for SubpixelOffset { + fn into(self) -> f64 { + match self { + SubpixelOffset::Zero => 0.0, + SubpixelOffset::Quarter => 0.25, + SubpixelOffset::Half => 0.5, + SubpixelOffset::ThreeQuarters => 0.75, + } + } +} + +#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct GlyphKey { + pub index: u32, + pub subpixel_offset: (SubpixelOffset, SubpixelOffset), +} + +impl GlyphKey { + pub fn new( + index: u32, + point: DevicePoint, + subpx_dir: SubpixelDirection, + ) -> GlyphKey { + let (dx, dy) = match subpx_dir { + SubpixelDirection::None => (0.0, 0.0), + SubpixelDirection::Horizontal => (point.x, 0.0), + SubpixelDirection::Vertical => (0.0, point.y), + SubpixelDirection::Mixed => (point.x, point.y), + }; + + GlyphKey { + index, + subpixel_offset: ( + SubpixelOffset::quantize(dx), + SubpixelOffset::quantize(dy), + ), + } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -395,11 +545,17 @@ impl GlyphRasterizer { pub fn get_glyph_dimensions( &mut self, font: &FontInstance, - glyph_key: &GlyphKey, + glyph_index: GlyphIndex, ) -> Option { + let glyph_key = GlyphKey::new( + glyph_index, + DevicePoint::zero(), + SubpixelDirection::None, + ); + self.font_contexts .lock_shared_context() - .get_glyph_dimensions(font, glyph_key) + .get_glyph_dimensions(font, &glyph_key) } pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option { @@ -454,23 +610,6 @@ impl AddFont for FontContext { } } -#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct GlyphRequest { - pub key: GlyphKey, - pub font: FontInstance, -} - -impl GlyphRequest { - pub fn new(font: &FontInstance, key: &GlyphKey) -> Self { - GlyphRequest { - key: key.clone(), - font: font.clone(), - } - } -} - #[allow(dead_code)] pub(in glyph_rasterizer) struct GlyphRasterJob { key: GlyphKey, @@ -511,13 +650,13 @@ mod test_glyph_rasterizer { use api::DeviceIntSize; use render_task::{RenderTaskCache, RenderTaskTree}; use profiler::TextureCacheProfileCounters; - use api::{FontKey, FontTemplate, FontRenderMode, GlyphKey, - IdNamespace, LayoutPoint, ColorF, ColorU, SubpixelDirection}; + use api::{FontKey, FontTemplate, FontRenderMode, + IdNamespace, ColorF, ColorU, DevicePoint}; use render_backend::FrameId; use app_units::Au; use thread_profiler::register_thread_with_profiler; use std::sync::Arc; - use glyph_rasterizer::{GlyphRasterizer, FontInstance}; + use glyph_rasterizer::{FontInstance, GlyphKey, GlyphRasterizer}; let worker = ThreadPoolBuilder::new() .thread_name(|idx|{ format!("WRWorker#{}", idx) }) @@ -550,19 +689,18 @@ mod test_glyph_rasterizer { ColorF::new(0.0, 0.0, 0.0, 1.0), ColorU::new(0, 0, 0, 0), FontRenderMode::Subpixel, - SubpixelDirection::Horizontal, Default::default(), None, Vec::new(), ); + let subpx_dir = font.get_subpx_dir(); let mut glyph_keys = Vec::with_capacity(200); for i in 0 .. 200 { glyph_keys.push(GlyphKey::new( i, - LayoutPoint::zero(), - font.render_mode, - font.subpx_dir, + DevicePoint::zero(), + subpx_dir, )); } @@ -590,4 +728,48 @@ mod test_glyph_rasterizer { &mut TextureCacheProfileCounters::new(), ); } -} \ No newline at end of file + + #[test] + fn test_subpx_quantize() { + use glyph_rasterizer::SubpixelOffset; + + assert_eq!(SubpixelOffset::quantize(0.0), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(-0.0), SubpixelOffset::Zero); + + assert_eq!(SubpixelOffset::quantize(0.1), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.01), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.05), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.12), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.124), SubpixelOffset::Zero); + + assert_eq!(SubpixelOffset::quantize(0.125), SubpixelOffset::Quarter); + assert_eq!(SubpixelOffset::quantize(0.2), SubpixelOffset::Quarter); + assert_eq!(SubpixelOffset::quantize(0.25), SubpixelOffset::Quarter); + assert_eq!(SubpixelOffset::quantize(0.33), SubpixelOffset::Quarter); + assert_eq!(SubpixelOffset::quantize(0.374), SubpixelOffset::Quarter); + + assert_eq!(SubpixelOffset::quantize(0.375), SubpixelOffset::Half); + assert_eq!(SubpixelOffset::quantize(0.4), SubpixelOffset::Half); + assert_eq!(SubpixelOffset::quantize(0.5), SubpixelOffset::Half); + assert_eq!(SubpixelOffset::quantize(0.58), SubpixelOffset::Half); + assert_eq!(SubpixelOffset::quantize(0.624), SubpixelOffset::Half); + + assert_eq!(SubpixelOffset::quantize(0.625), SubpixelOffset::ThreeQuarters); + assert_eq!(SubpixelOffset::quantize(0.67), SubpixelOffset::ThreeQuarters); + assert_eq!(SubpixelOffset::quantize(0.7), SubpixelOffset::ThreeQuarters); + assert_eq!(SubpixelOffset::quantize(0.78), SubpixelOffset::ThreeQuarters); + assert_eq!(SubpixelOffset::quantize(0.874), SubpixelOffset::ThreeQuarters); + + assert_eq!(SubpixelOffset::quantize(0.875), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.89), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.91), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.967), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(0.999), SubpixelOffset::Zero); + + assert_eq!(SubpixelOffset::quantize(-1.0), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(1.0), SubpixelOffset::Zero); + assert_eq!(SubpixelOffset::quantize(1.5), SubpixelOffset::Half); + assert_eq!(SubpixelOffset::quantize(-1.625), SubpixelOffset::Half); + assert_eq!(SubpixelOffset::quantize(-4.33), SubpixelOffset::ThreeQuarters); + } +} diff --git a/gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs b/gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs index 9f6f8fae5960..a66b9ef132c3 100644 --- a/gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs +++ b/gfx/webrender/src/glyph_rasterizer/no_pathfinder.rs @@ -5,14 +5,15 @@ //! Module only available when pathfinder is deactivated when webrender is //! compiled regularly (i.e. any configuration without feature = "pathfinder") -use api::{GlyphKey, ImageData, ImageDescriptor, ImageFormat}; +use api::{ImageData, ImageDescriptor, ImageFormat}; use device::TextureFilter; use euclid::size2; use gpu_types::UvRectKind; use rayon::prelude::*; use std::sync::{Arc, MutexGuard}; use platform::font::FontContext; -use glyph_rasterizer::{FontInstance, FontContexts, GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult}; +use glyph_rasterizer::{FontInstance, FontContexts, GlyphKey}; +use glyph_rasterizer::{GlyphRasterizer, GlyphRasterJob, GlyphRasterJobs, GlyphRasterResult}; use glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry}; use texture_cache::{TextureCache, TextureCacheHandle}; use gpu_cache::GpuCache; @@ -202,4 +203,4 @@ impl GlyphRasterizer { // we can schedule removing the fonts if needed. self.remove_dead_fonts(); } -} \ No newline at end of file +} diff --git a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs index b741f90d9f72..6f4ff494465e 100644 --- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs +++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs @@ -4,7 +4,7 @@ //! Module only available when pathfinder is activated -use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, GlyphKey, NativeFontHandle}; +use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, NativeFontHandle}; use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D}; use pathfinder_font_renderer; use pathfinder_partitioner::mesh::Mesh as PathfinderMesh; @@ -20,7 +20,7 @@ use internal_types::ResourceCacheError; use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo}; use std::collections::hash_map::Entry; use std::f32; -use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, FontContexts}; +use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, GlyphKey, FontContexts}; use texture_cache::TextureCache; use gpu_cache::GpuCache; use profiler::TextureCacheProfileCounters; @@ -190,8 +190,9 @@ impl GlyphRasterizer { size: font.size, }; + // TODO: pathfinder will need to support 2D subpixel offset let pathfinder_subpixel_offset = - pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8); + pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8); let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index, pathfinder_subpixel_offset); @@ -272,9 +273,10 @@ fn request_render_task_from_pathfinder(glyph_key: &GlyphKey, size: font.size, }; + // TODO: pathfinder will need to support 2D subpixel offset let pathfinder_subpixel_offset = - pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset as u8); - let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.into(); + pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8); + let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into(); let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index, pathfinder_subpixel_offset); @@ -311,4 +313,4 @@ fn request_render_task_from_pathfinder(glyph_key: &GlyphKey, Ok(root_task_id) } -pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle); \ No newline at end of file +pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle); diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index d1a1b33e4bb5..741e8033af09 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -2,7 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{DevicePoint, LayoutToWorldTransform, WorldToLayoutTransform}; +use api::{DevicePoint, DeviceSize, DeviceRect, LayoutToWorldTransform}; +use api::{PremultipliedColorF, WorldToLayoutTransform}; use gpu_cache::{GpuCacheAddress, GpuDataRequest}; use prim_store::{VECS_PER_SEGMENT, EdgeAaSegmentMask}; use render_task::RenderTaskAddress; @@ -79,6 +80,35 @@ pub struct BlurInstance { pub blur_direction: BlurDirection, } +#[derive(Debug, Copy, Clone)] +#[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum BorderSegment { + TopLeft, + TopRight, + BottomRight, + BottomLeft, + Left, + Top, + Right, + Bottom, +} + +#[derive(Debug, Clone)] +#[repr(C)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderInstance { + pub task_origin: DevicePoint, + pub local_rect: DeviceRect, + pub color0: PremultipliedColorF, + pub color1: PremultipliedColorF, + pub flags: i32, + pub widths: DeviceSize, + pub radius: DeviceSize, +} + /// A clipping primitive drawn into the clipping mask. /// Could be an image or a rectangle, which defines the /// way `address` is treated. diff --git a/gfx/webrender/src/platform/macos/font.rs b/gfx/webrender/src/platform/macos/font.rs index c5e3535b91bd..d21f3fb37b9c 100644 --- a/gfx/webrender/src/platform/macos/font.rs +++ b/gfx/webrender/src/platform/macos/font.rs @@ -4,7 +4,6 @@ use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions}; use api::{FontInstanceFlags, FontVariation, NativeFontHandle}; -use api::{GlyphKey, SubpixelDirection}; use app_units::Au; use core_foundation::array::{CFArray, CFArrayRef}; use core_foundation::base::TCFType; @@ -27,7 +26,7 @@ use core_text; use core_text::font::{CTFont, CTFontRef}; use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait}; use gamma_lut::{ColorLut, GammaLut}; -use glyph_rasterizer::{FontInstance, FontTransform}; +use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey}; #[cfg(feature = "pathfinder")] use glyph_rasterizer::NativeFontHandleWrapper; #[cfg(not(feature = "pathfinder"))] @@ -463,7 +462,7 @@ impl FontContext { // In mono mode the color of the font is irrelevant. font.color = ColorU::new(255, 255, 255, 255); // Subpixel positioning is disabled in mono mode. - font.subpx_dir = SubpixelDirection::None; + font.disable_subpixel_position(); } FontRenderMode::Alpha => { font.color = if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) { diff --git a/gfx/webrender/src/platform/unix/font.rs b/gfx/webrender/src/platform/unix/font.rs index d1a352071170..c82b777287a8 100644 --- a/gfx/webrender/src/platform/unix/font.rs +++ b/gfx/webrender/src/platform/unix/font.rs @@ -2,9 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ColorU, GlyphDimensions, GlyphKey, FontKey, FontRenderMode}; +use api::{ColorU, GlyphDimensions, FontKey, FontRenderMode}; use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting}; -use api::{FontInstanceFlags, NativeFontHandle, SubpixelDirection}; +use api::{FontInstanceFlags, NativeFontHandle}; use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode}; use freetype::freetype::{FT_Done_Face, FT_Error, FT_Get_Char_Index, FT_Int32}; use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter, FT_Pos}; @@ -18,7 +18,7 @@ use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHIN use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT}; use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES}; use freetype::succeeded; -use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterResult, RasterizedGlyph}; +use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterResult, RasterizedGlyph}; #[cfg(feature = "pathfinder")] use glyph_rasterizer::NativeFontHandleWrapper; use internal_types::{FastHashMap, ResourceCacheError}; @@ -253,9 +253,10 @@ impl FontContext { (FontHinting::Mono, _) => load_flags = FT_LOAD_TARGET_MONO, (FontHinting::Light, _) => load_flags = FT_LOAD_TARGET_LIGHT, (FontHinting::LCD, FontRenderMode::Subpixel) => { - load_flags = match font.subpx_dir { - SubpixelDirection::Vertical => FT_LOAD_TARGET_LCD_V, - _ => FT_LOAD_TARGET_LCD, + load_flags = if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) { + FT_LOAD_TARGET_LCD_V + } else { + FT_LOAD_TARGET_LCD }; if font.flags.contains(FontInstanceFlags::FORCE_AUTOHINT) { load_flags |= FT_LOAD_FORCE_AUTOHINT; @@ -375,31 +376,29 @@ impl FontContext { } } - // Convert the subpixel offset to floats. - let (dx, dy) = font.get_subpx_offset(glyph); - // Apply extra pixel of padding for subpixel AA, due to the filter. - let padding = match font.render_mode { - FontRenderMode::Subpixel => (self.lcd_extra_pixels * 64) as FT_Pos, - FontRenderMode::Alpha | - FontRenderMode::Mono => 0 as FT_Pos, - }; + if font.render_mode == FontRenderMode::Subpixel { + let padding = (self.lcd_extra_pixels * 64) as FT_Pos; + if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) { + cbox.yMin -= padding; + cbox.yMax += padding; + } else { + cbox.xMin -= padding; + cbox.xMax += padding; + } + } // Offset the bounding box by subpixel positioning. // Convert to 26.6 fixed point format for FT. - match font.subpx_dir { - SubpixelDirection::None => {} - SubpixelDirection::Horizontal => { - let dx = (dx * 64.0 + 0.5) as FT_Long; - cbox.xMin += dx - padding; - cbox.xMax += dx + padding; - } - SubpixelDirection::Vertical => { - let dy = (dy * 64.0 + 0.5) as FT_Long; - cbox.yMin += dy - padding; - cbox.yMax += dy + padding; - } - } + let (dx, dy) = font.get_subpx_offset(glyph); + let (dx, dy) = ( + (dx * 64.0 + 0.5) as FT_Pos, + -(dy * 64.0 + 0.5) as FT_Pos, + ); + cbox.xMin += dx; + cbox.xMax += dx; + cbox.yMin += dy; + cbox.yMax += dy; // Outset the box to device pixel boundaries cbox.xMin &= !63; @@ -522,7 +521,7 @@ impl FontContext { // In mono mode the color of the font is irrelevant. font.color = ColorU::new(0xFF, 0xFF, 0xFF, 0xFF); // Subpixel positioning is disabled in mono mode. - font.subpx_dir = SubpixelDirection::None; + font.disable_subpixel_position(); } FontRenderMode::Alpha | FontRenderMode::Subpixel => { // We don't do any preblending with FreeType currently, so the color is not used. @@ -539,8 +538,10 @@ impl FontContext { ) -> bool { // Get the subpixel offsets in FT 26.6 format. let (dx, dy) = font.get_subpx_offset(key); - let dx = (dx * 64.0 + 0.5) as FT_Long; - let dy = (dy * 64.0 + 0.5) as FT_Long; + let (dx, dy) = ( + (dx * 64.0 + 0.5) as FT_Pos, + -(dy * 64.0 + 0.5) as FT_Pos, + ); // Move the outline curves to be at the origin, taking // into account the subpixel positioning. @@ -565,11 +566,14 @@ impl FontContext { }; unsafe { FT_Library_SetLcdFilter(self.lib, filter) }; } - let render_mode = match (font.render_mode, font.subpx_dir) { - (FontRenderMode::Mono, _) => FT_Render_Mode::FT_RENDER_MODE_MONO, - (FontRenderMode::Alpha, _) => FT_Render_Mode::FT_RENDER_MODE_NORMAL, - (FontRenderMode::Subpixel, SubpixelDirection::Vertical) => FT_Render_Mode::FT_RENDER_MODE_LCD_V, - (FontRenderMode::Subpixel, _) => FT_Render_Mode::FT_RENDER_MODE_LCD, + let render_mode = match font.render_mode { + FontRenderMode::Mono => FT_Render_Mode::FT_RENDER_MODE_MONO, + FontRenderMode::Alpha => FT_Render_Mode::FT_RENDER_MODE_NORMAL, + FontRenderMode::Subpixel => if font.flags.contains(FontInstanceFlags::LCD_VERTICAL) { + FT_Render_Mode::FT_RENDER_MODE_LCD_V + } else { + FT_Render_Mode::FT_RENDER_MODE_LCD + }, }; let result = unsafe { FT_Render_Glyph(slot, render_mode) }; if !succeeded(result) { diff --git a/gfx/webrender/src/platform/windows/font.rs b/gfx/webrender/src/platform/windows/font.rs index 0aca370c2551..9808c9acf458 100644 --- a/gfx/webrender/src/platform/windows/font.rs +++ b/gfx/webrender/src/platform/windows/font.rs @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{FontInstanceFlags, FontKey, FontRenderMode}; -use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection}; +use api::{ColorU, GlyphDimensions}; use dwrote; use gamma_lut::ColorLut; -use glyph_rasterizer::{FontInstance, FontTransform}; +use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey}; use internal_types::{FastHashMap, ResourceCacheError}; use std::collections::hash_map::Entry; use std::sync::Arc; @@ -372,7 +372,7 @@ impl FontContext { // In mono mode the color of the font is irrelevant. font.color = ColorU::new(255, 255, 255, 255); // Subpixel positioning is disabled in mono mode. - font.subpx_dir = SubpixelDirection::None; + font.disable_subpixel_position(); } FontRenderMode::Alpha => { font.color = font.color.luminance_color().quantize(); @@ -469,4 +469,4 @@ impl<'a> From> for PathfinderComPtr let face = font.create_font_face(); unsafe { PathfinderComPtr::new(face.as_ptr()) } } -} \ No newline at end of file +} diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index f56281d0316e..848d59e2b0b2 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -3,11 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion}; -use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode, FontRenderMode}; -use api::{FilterOp, GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; +use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode}; +use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D}; use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets}; -use border::{BorderCornerInstance, BorderEdgeKind}; +use api::{BorderWidths, LayoutToWorldScale, NormalBorder}; +use app_units::Au; +use border::{BorderCacheKey, BorderCornerInstance, BorderRenderTaskInfo, BorderEdgeKind}; use box_shadow::BLUR_SAMPLE_SCALE; use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId}; use clip_scroll_node::ClipScrollNode; @@ -15,7 +17,7 @@ use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSo use clip::{ClipSourcesHandle, ClipWorkItem}; use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; use frame_builder::PrimitiveRunContext; -use glyph_rasterizer::{FontInstance, FontTransform}; +use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use gpu_types::{BrushFlags, ClipChainRectIndex}; @@ -255,6 +257,18 @@ pub struct VisibleGradientTile { pub handle: GpuCacheHandle, } +#[derive(Debug)] +pub enum BorderSource { + Image(ImageRequest), + Border { + handle: Option, + cache_key: BorderCacheKey, + task_info: Option, + border: NormalBorder, + widths: BorderWidths, + }, +} + #[derive(Debug)] pub enum BrushKind { Solid { @@ -307,7 +321,7 @@ pub enum BrushKind { visible_tiles: Vec, }, Border { - request: ImageRequest, + source: BorderSource, }, } @@ -353,7 +367,7 @@ bitflags! { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum BrushSegmentTaskId { RenderTaskId(RenderTaskId), Opaque, @@ -369,7 +383,7 @@ impl BrushSegmentTaskId { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct BrushSegment { pub local_rect: LayoutRect, pub clip_task_id: BrushSegmentTaskId, @@ -381,15 +395,14 @@ pub struct BrushSegment { impl BrushSegment { pub fn new( - origin: LayoutPoint, - size: LayoutSize, + rect: LayoutRect, may_need_clip_mask: bool, edge_flags: EdgeAaSegmentMask, extra_data: [f32; 4], brush_flags: BrushFlags, ) -> BrushSegment { BrushSegment { - local_rect: LayoutRect::new(origin, size), + local_rect: rect, clip_task_id: BrushSegmentTaskId::Opaque, may_need_clip_mask, edge_flags, @@ -761,11 +774,32 @@ pub struct TextRunPrimitiveCpu { pub glyph_range: ItemRange, pub glyph_keys: Vec, pub glyph_gpu_blocks: Vec, + pub glyph_transform: (DevicePixelScale, FontTransform), pub shadow: bool, pub glyph_raster_space: GlyphRasterSpace, } impl TextRunPrimitiveCpu { + pub fn new( + font: FontInstance, + offset: LayoutVector2D, + glyph_range: ItemRange, + glyph_keys: Vec, + shadow: bool, + glyph_raster_space: GlyphRasterSpace, + ) -> Self { + TextRunPrimitiveCpu { + font, + offset, + glyph_range, + glyph_keys, + glyph_gpu_blocks: Vec::new(), + glyph_transform: (DevicePixelScale::new(1.0), FontTransform::identity()), + shadow, + glyph_raster_space, + } + } + pub fn get_font( &self, device_pixel_scale: DevicePixelScale, @@ -777,7 +811,8 @@ impl TextRunPrimitiveCpu { if transform.has_perspective_component() || !transform.has_2d_inverse() || self.glyph_raster_space != GlyphRasterSpace::Screen { - font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha); + font.disable_subpixel_aa(); + font.disable_subpixel_position(); } else { font.transform = FontTransform::from(&transform).quantize(); } @@ -794,7 +829,7 @@ impl TextRunPrimitiveCpu { frame_building_state: &mut FrameBuildingState, ) { if !allow_subpixel_aa && self.font.bg_color.a == 0 { - self.font.render_mode = self.font.render_mode.limit_by(FontRenderMode::Alpha); + self.font.disable_subpixel_aa(); } let font = self.get_font(device_pixel_scale, transform); @@ -803,8 +838,8 @@ impl TextRunPrimitiveCpu { // TODO(gw): In the future, remove `glyph_instances` // completely, and just reference the glyphs // directly from the display list. - if self.glyph_keys.is_empty() { - let subpx_dir = font.subpx_dir.limit_by(font.render_mode); + if self.glyph_keys.is_empty() || self.glyph_transform != (device_pixel_scale, font.transform) { + let subpx_dir = font.get_subpx_dir(); let src_glyphs = display_list.get(self.glyph_range); // TODO(gw): If we support chunks() on AuxIter @@ -812,7 +847,10 @@ impl TextRunPrimitiveCpu { // be much simpler... let mut gpu_block = [0.0; 4]; for (i, src) in src_glyphs.enumerate() { - let key = GlyphKey::new(src.index, src.point, font.render_mode, subpx_dir); + let layout_offset = src.point + self.offset; + let world_offset = font.transform.transform(&layout_offset); + let device_offset = device_pixel_scale.transform_point(&world_offset); + let key = GlyphKey::new(src.index, device_offset, subpx_dir); self.glyph_keys.push(key); // Two glyphs are packed per GPU block. @@ -832,6 +870,8 @@ impl TextRunPrimitiveCpu { if (self.glyph_keys.len() & 1) != 0 { self.glyph_gpu_blocks.push(gpu_block.into()); } + + self.glyph_transform = (device_pixel_scale, font.transform); } frame_building_state.resource_cache @@ -1090,25 +1130,22 @@ impl PrimitiveContainer { pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer { match *self { PrimitiveContainer::TextRun(ref info) => { - let mut render_mode = info.font.render_mode; - + let mut font = FontInstance { + color: shadow.color.into(), + ..info.font.clone() + }; if shadow.blur_radius > 0.0 { - render_mode = render_mode.limit_by(FontRenderMode::Alpha); + font.disable_subpixel_aa(); } - PrimitiveContainer::TextRun(TextRunPrimitiveCpu { - font: FontInstance { - color: shadow.color.into(), - render_mode, - ..info.font.clone() - }, - offset: info.offset + shadow.offset, - glyph_range: info.glyph_range, - glyph_keys: info.glyph_keys.clone(), - glyph_gpu_blocks: Vec::new(), - shadow: true, - glyph_raster_space: info.glyph_raster_space, - }) + PrimitiveContainer::TextRun(TextRunPrimitiveCpu::new( + font, + info.offset + shadow.offset, + info.glyph_range, + info.glyph_keys.clone(), + true, + info.glyph_raster_space, + )) } PrimitiveContainer::Brush(ref brush) => { match brush.kind { @@ -1401,6 +1438,84 @@ impl PrimitiveStore { self.cpu_metadata.len() } + fn build_prim_segments_if_needed( + &mut self, + prim_index: PrimitiveIndex, + pic_state: &mut PictureState, + frame_state: &mut FrameBuildingState, + frame_context: &FrameBuildingContext, + ) { + let metadata = &self.cpu_metadata[prim_index.0]; + + if metadata.prim_kind != PrimitiveKind::Brush { + return; + } + + let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0]; + + if let BrushKind::Border { ref mut source, .. } = brush.kind { + if let BorderSource::Border { + ref border, + ref mut cache_key, + ref widths, + ref mut handle, + ref mut task_info, + .. + } = *source { + // TODO(gw): When drawing in screen raster mode, we should also incorporate a + // scale factor from the world transform to get an appropriately + // sized border task. + let world_scale = LayoutToWorldScale::new(1.0); + let scale = world_scale * frame_context.device_pixel_scale; + let scale_au = Au::from_f32_px(scale.0); + let needs_update = scale_au != cache_key.scale; + + if needs_update { + cache_key.scale = scale_au; + + *task_info = Some(BorderRenderTaskInfo::new( + &metadata.local_rect, + border, + widths, + scale, + )); + } + + let task_info = task_info.as_ref().unwrap(); + + *handle = Some(frame_state.resource_cache.request_render_task( + RenderTaskCacheKey { + size: DeviceIntSize::zero(), + kind: RenderTaskCacheKeyKind::Border(cache_key.clone()), + }, + frame_state.gpu_cache, + frame_state.render_tasks, + None, + false, // todo + |render_tasks| { + let task = RenderTask::new_border( + task_info.size, + task_info.instances.clone(), + ); + + let task_id = render_tasks.add(task); + + pic_state.tasks.push(task_id); + + task_id + } + )); + + if needs_update { + brush.segment_desc = Some(BrushSegmentDescriptor { + segments: task_info.segments.clone(), + clip_mask_kind: BrushClipMaskKind::Unknown, + }); + } + } + } + } + fn prepare_prim_for_render_inner( &mut self, prim_index: PrimitiveIndex, @@ -1663,21 +1778,29 @@ impl PrimitiveStore { ); } } - BrushKind::Border { request, .. } => { - let image_properties = frame_state - .resource_cache - .get_image_properties(request.key); + BrushKind::Border { ref mut source, .. } => { + match *source { + BorderSource::Image(request) => { + let image_properties = frame_state + .resource_cache + .get_image_properties(request.key); - if let Some(image_properties) = image_properties { - // Update opacity for this primitive to ensure the correct - // batching parameters are used. - metadata.opacity.is_opaque = - image_properties.descriptor.is_opaque; + if let Some(image_properties) = image_properties { + // Update opacity for this primitive to ensure the correct + // batching parameters are used. + metadata.opacity.is_opaque = + image_properties.descriptor.is_opaque; - frame_state.resource_cache.request_image( - request, - frame_state.gpu_cache, - ); + frame_state.resource_cache.request_image( + request, + frame_state.gpu_cache, + ); + } + } + BorderSource::Border { .. } => { + // Handled earlier since we need to update the segment + // descriptor *before* update_clip_task() is called. + } } } BrushKind::RadialGradient { @@ -2004,8 +2127,7 @@ impl PrimitiveStore { segment_builder.build(|segment| { segments.push( BrushSegment::new( - segment.rect.origin, - segment.rect.size, + segment.rect, segment.has_mask, segment.edge_flags, [0.0; 4], @@ -2390,6 +2512,13 @@ impl PrimitiveStore { (local_rect, unclipped) }; + self.build_prim_segments_if_needed( + prim_index, + pic_state, + frame_state, + frame_context, + ); + if may_need_clip_mask && !self.update_clip_task( prim_index, prim_run_context, diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index c2e2dd8c72cd..7a67cc1b8e7c 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -813,11 +813,11 @@ impl RenderBackend { self.resource_cache .update_resources(updates, &mut profile_counters.resources); } - ApiMsg::GetGlyphDimensions(instance_key, glyph_keys, tx) => { - let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len()); + ApiMsg::GetGlyphDimensions(instance_key, glyph_indices, tx) => { + let mut glyph_dimensions = Vec::with_capacity(glyph_indices.len()); if let Some(font) = self.resource_cache.get_font_instance(instance_key) { - for glyph_key in &glyph_keys { - let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, glyph_key); + for glyph_index in &glyph_indices { + let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index); glyph_dimensions.push(glyph_dim); } } diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 9ad053516397..cd4e1b29a615 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -5,6 +5,7 @@ use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSideOffsets, ImageDescriptor, ImageFormat}; #[cfg(feature = "pathfinder")] use api::FontRenderMode; +use border::BorderCacheKey; use box_shadow::{BoxShadowCacheKey}; use clip::{ClipSource, ClipStore, ClipWorkItem}; use clip_scroll_tree::CoordinateSystemId; @@ -14,7 +15,7 @@ use euclid::{TypedPoint2D, TypedVector2D}; use freelist::{FreeList, FreeListHandle, WeakFreeListHandle}; use glyph_rasterizer::GpuGlyphCacheKey; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; -use gpu_types::{ImageSource, RasterizationSpace, UvRectKind}; +use gpu_types::{BorderInstance, ImageSource, RasterizationSpace, UvRectKind}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; #[cfg(feature = "pathfinder")] use pathfinder_partitioner::mesh::Mesh; @@ -249,6 +250,13 @@ pub enum BlitSource { }, } +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct BorderTask { + pub instances: Vec, +} + #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -278,6 +286,7 @@ pub enum RenderTaskKind { Readback(DeviceIntRect), Scaling(RenderTargetKind), Blit(BlitTask), + Border(BorderTask), } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] @@ -557,6 +566,21 @@ impl RenderTask { } } + pub fn new_border( + size: DeviceIntSize, + instances: Vec, + ) -> Self { + RenderTask { + children: Vec::new(), + location: RenderTaskLocation::Dynamic(None, Some(size)), + kind: RenderTaskKind::Border(BorderTask { + instances, + }), + clear_mode: ClearMode::Transparent, + saved_index: None, + } + } + pub fn new_scaling( target_kind: RenderTargetKind, src_task_id: RenderTaskId, @@ -616,6 +640,7 @@ impl RenderTask { RenderTaskKind::ClipRegion(..) | RenderTaskKind::Glyph(_) | + RenderTaskKind::Border(..) | RenderTaskKind::Blit(..) => { UvRectKind::Rect } @@ -670,6 +695,7 @@ impl RenderTask { } RenderTaskKind::Readback(..) | RenderTaskKind::Scaling(..) | + RenderTaskKind::Border(..) | RenderTaskKind::Blit(..) => { [0.0; 3] } @@ -704,6 +730,7 @@ impl RenderTask { RenderTaskKind::Readback(..) | RenderTaskKind::Scaling(..) | RenderTaskKind::Blit(..) | + RenderTaskKind::Border(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::Glyph(..) => { panic!("texture handle not supported for this task kind"); @@ -776,6 +803,7 @@ impl RenderTask { target_kind } + RenderTaskKind::Border(..) | RenderTaskKind::Picture(..) => { RenderTargetKind::Color } @@ -801,6 +829,7 @@ impl RenderTask { RenderTaskKind::Scaling(..) | RenderTaskKind::ClipRegion(..) | RenderTaskKind::Blit(..) | + RenderTaskKind::Border(..) | RenderTaskKind::Glyph(..) => false, // TODO(gw): For now, we've disabled the shared clip mask @@ -837,6 +866,7 @@ impl RenderTask { RenderTaskKind::Scaling(..) | RenderTaskKind::Blit(..) | RenderTaskKind::ClipRegion(..) | + RenderTaskKind::Border(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::Glyph(..) => { return; @@ -887,6 +917,9 @@ impl RenderTask { pt.new_level("Scaling".to_owned()); pt.add_item(format!("kind: {:?}", kind)); } + RenderTaskKind::Border(..) => { + pt.new_level("Border".to_owned()); + } RenderTaskKind::Blit(ref task) => { pt.new_level("Blit".to_owned()); pt.add_item(format!("source: {:?}", task.source)); @@ -931,6 +964,7 @@ pub enum RenderTaskCacheKeyKind { #[allow(dead_code)] Glyph(GpuGlyphCacheKey), Picture(PictureCacheKey), + Border(BorderCacheKey), } #[derive(Clone, Debug, Hash, PartialEq, Eq)] diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 9ff55c89dd2d..d02fa28a127f 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -128,6 +128,10 @@ const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE, }; +const GPU_TAG_CACHE_BORDER: GpuProfileTag = GpuProfileTag { + label: "C_Border", + color: debug_colors::CORNSILK, +}; const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "target init", color: debug_colors::SLATEGREY, @@ -390,6 +394,53 @@ pub(crate) mod desc { ], }; + pub const BORDER: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aTaskOrigin", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor0", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor1", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aFlags", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aWidths", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aRadii", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + }; + pub const CLIP: VertexDescriptor = VertexDescriptor { vertex_attributes: &[ VertexAttribute { @@ -573,6 +624,7 @@ pub(crate) enum VertexArrayKind { DashAndDot, VectorStencil, VectorCover, + Border, } #[derive(Clone, Debug, PartialEq)] @@ -1293,6 +1345,7 @@ pub struct RendererVAOs { blur_vao: VAO, clip_vao: VAO, dash_and_dot_vao: VAO, + border_vao: VAO, } /// The renderer is responsible for submitting to the GPU the work prepared by the @@ -1579,6 +1632,8 @@ impl Renderer { let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao); let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao); + let border_vao = + device.create_vao_with_new_instances(&desc::BORDER, &prim_vao); let dash_and_dot_vao = device.create_vao_with_new_instances(&desc::BORDER_CORNER_DASH_AND_DOT, &prim_vao); let texture_cache_upload_pbo = device.create_pbo(); @@ -1734,6 +1789,7 @@ impl Renderer { blur_vao, clip_vao, dash_and_dot_vao, + border_vao, }, node_data_texture, local_clip_rects_texture, @@ -3248,9 +3304,36 @@ impl Renderer { self.device.disable_depth_write(); self.device.set_blend(false); + for rect in &target.clears { + self.device.clear_target(Some([0.0, 0.0, 0.0, 0.0]), None, Some(*rect)); + } + // Handle any blits to this texture from child tasks. self.handle_blits(&target.blits, render_tasks); + // Draw any borders for this target. + if !target.border_segments.is_empty() { + let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_BORDER); + + self.device.set_blend(true); + self.device.set_blend_mode_premultiplied_alpha(); + + self.shaders.cs_border_segment.bind( + &mut self.device, + &projection, + &mut self.renderer_errors, + ); + + self.draw_instanced_batch( + &target.border_segments, + VertexArrayKind::Border, + &BatchTextures::no_texture(), + stats, + ); + + self.device.set_blend(false); + } + // Draw any blurs for this target. if !target.horizontal_blurs.is_empty() { let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); @@ -3853,6 +3936,7 @@ impl Renderer { self.device.delete_vao(self.vaos.clip_vao); self.device.delete_vao(self.vaos.blur_vao); self.device.delete_vao(self.vaos.dash_and_dot_vao); + self.device.delete_vao(self.vaos.border_vao); #[cfg(feature = "debug_renderer")] { @@ -4448,6 +4532,7 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind, VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao, VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao, VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao, + VertexArrayKind::Border => &vaos.border_vao, } } @@ -4462,5 +4547,6 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind, VertexArrayKind::Blur => &vaos.blur_vao, VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao, VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(), + VertexArrayKind::Border => &vaos.border_vao, } } diff --git a/gfx/webrender/src/resource_cache.rs b/gfx/webrender/src/resource_cache.rs index 819d2537afa2..50693cc5182d 100644 --- a/gfx/webrender/src/resource_cache.rs +++ b/gfx/webrender/src/resource_cache.rs @@ -2,13 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{AddFont, BlobImageResources, ResourceUpdate, ResourceUpdates}; +use api::{AddFont, BlobImageResources, ResourceUpdate}; use api::{BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest}; use api::{ClearCache, ColorF, DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; -use api::{Epoch, FontInstanceKey, FontKey, FontTemplate}; +use api::{Epoch, FontInstanceKey, FontKey, FontTemplate, GlyphIndex}; use api::{ExternalImageData, ExternalImageType}; use api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation}; -use api::{GlyphDimensions, GlyphKey, IdNamespace}; +use api::{GlyphDimensions, IdNamespace}; use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering}; use api::{TileOffset, TileSize}; use app_units::Au; @@ -23,7 +23,7 @@ use euclid::size2; use glyph_cache::GlyphCache; #[cfg(not(feature = "pathfinder"))] use glyph_cache::GlyphCacheEntry; -use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphRasterizer, GlyphRequest}; +use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer}; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use gpu_types::UvRectKind; use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList}; @@ -174,7 +174,7 @@ where K: Clone + Hash + Eq + Debug, U: Default, { - pub fn new() -> ResourceClassCache { + pub fn new() -> Self { ResourceClassCache { resources: FastHashMap::default(), user_data: Default::default(), @@ -282,7 +282,7 @@ impl BlobImageResources for Resources { } } -pub type GlyphDimensionsCache = FastHashMap>; +pub type GlyphDimensionsCache = FastHashMap<(FontInstance, GlyphIndex), Option>; pub struct ResourceCache { cached_glyphs: GlyphCache, @@ -371,14 +371,14 @@ impl ResourceCache { pub fn update_resources( &mut self, - updates: ResourceUpdates, + updates: Vec, profile_counters: &mut ResourceProfileCounters, ) { // TODO, there is potential for optimization here, by processing updates in // bulk rather than one by one (for example by sorting allocations by size or // in a way that reduces fragmentation in the atlas). - for update in updates.updates { + for update in updates { match update { ResourceUpdate::AddImage(img) => { if let ImageData::Raw(ref bytes) = img.data { @@ -449,7 +449,6 @@ impl ResourceCache { ) { let FontInstanceOptions { render_mode, - subpx_dir, flags, bg_color, .. @@ -460,7 +459,6 @@ impl ResourceCache { ColorF::new(0.0, 0.0, 0.0, 1.0), bg_color, render_mode, - subpx_dir, flags, platform_options, variations, @@ -510,16 +508,14 @@ impl ResourceCache { tiling, ); } + let dirty_rect = Some(descriptor.full_rect()); let resource = ImageResource { descriptor, data, epoch: Epoch(0), tiling, - dirty_rect: Some(DeviceUintRect::new( - DeviceUintPoint::zero(), - descriptor.size, - )), + dirty_rect, }; self.resources.image_templates.insert(image_key, resource); @@ -636,9 +632,14 @@ impl ResourceCache { let needs_upload = self.texture_cache .request(&entry.as_ref().unwrap().texture_cache_handle, gpu_cache); - if !needs_upload && !needs_update { - return; - } + let dirty_rect = if needs_upload { + // the texture cache entry has been evicted, treat it as all dirty + Some(template.descriptor.full_rect()) + } else if needs_update { + template.dirty_rect + } else { + return + }; // We can start a worker thread rasterizing right now, if: // - The image is a blob. @@ -658,7 +659,7 @@ impl ResourceCache { tile_offset.y as f32 * tile_size as f32, ); - if let Some(dirty) = template.dirty_rect { + if let Some(dirty) = dirty_rect { if intersect_for_tile(dirty, actual_size, tile_size, tile_offset).is_none() { // don't bother requesting unchanged tiles return @@ -678,7 +679,7 @@ impl ResourceCache { offset, format: template.descriptor.format, }, - template.dirty_rect, + dirty_rect, ); } } @@ -812,15 +813,13 @@ impl ResourceCache { pub fn get_glyph_dimensions( &mut self, font: &FontInstance, - key: &GlyphKey, + glyph_index: GlyphIndex, ) -> Option { - let key = GlyphRequest::new(font, key); - - match self.cached_glyph_dimensions.entry(key.clone()) { + match self.cached_glyph_dimensions.entry((font.clone(), glyph_index)) { Occupied(entry) => *entry.get(), Vacant(entry) => *entry.insert( self.glyph_rasterizer - .get_glyph_dimensions(&key.font, &key.key), + .get_glyph_dimensions(font, glyph_index), ), } } diff --git a/gfx/webrender/src/scene_builder.rs b/gfx/webrender/src/scene_builder.rs index fcdc1bdef054..7e7de5311e11 100644 --- a/gfx/webrender/src/scene_builder.rs +++ b/gfx/webrender/src/scene_builder.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdates}; +use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdate}; use api::channel::MsgSender; use display_list_flattener::build_scene; use frame_builder::{FrameBuilderConfig, FrameBuilder}; @@ -19,7 +19,7 @@ pub enum SceneBuilderRequest { Transaction { document_id: DocumentId, scene: Option, - resource_updates: ResourceUpdates, + resource_updates: Vec, frame_ops: Vec, render: bool, }, @@ -33,7 +33,7 @@ pub enum SceneBuilderResult { Transaction { document_id: DocumentId, built_scene: Option, - resource_updates: ResourceUpdates, + resource_updates: Vec, frame_ops: Vec, render: bool, result_tx: Option>, diff --git a/gfx/webrender/src/shade.rs b/gfx/webrender/src/shade.rs index 36ce26dcb911..9e33d3a3575e 100644 --- a/gfx/webrender/src/shade.rs +++ b/gfx/webrender/src/shade.rs @@ -403,6 +403,7 @@ fn create_prim_shader( VertexArrayKind::DashAndDot => desc::BORDER_CORNER_DASH_AND_DOT, VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL, VertexArrayKind::VectorCover => desc::VECTOR_COVER, + VertexArrayKind::Border => desc::BORDER, }; let program = device.create_program(name, &prefix, &vertex_descriptor); @@ -464,6 +465,7 @@ pub struct Shaders { // of these shaders are then used by the primitive shaders. pub cs_blur_a8: LazilyCompiledShader, pub cs_blur_rgba8: LazilyCompiledShader, + pub cs_border_segment: LazilyCompiledShader, // Brush shaders brush_solid: BrushShader, @@ -711,6 +713,14 @@ impl Shaders { options.precache_shaders, )?; + let cs_border_segment = LazilyCompiledShader::new( + ShaderKind::Cache(VertexArrayKind::Border), + "cs_border_segment", + &[], + device, + options.precache_shaders, + )?; + let ps_split_composite = LazilyCompiledShader::new( ShaderKind::Primitive, "ps_split_composite", @@ -726,6 +736,7 @@ impl Shaders { Ok(Shaders { cs_blur_a8, cs_blur_rgba8, + cs_border_segment, brush_solid, brush_image, brush_blend, @@ -844,6 +855,7 @@ impl Shaders { } self.ps_border_corner.deinit(device); self.ps_border_edge.deinit(device); + self.cs_border_segment.deinit(device); self.ps_split_composite.deinit(device); } } diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index a154961e330e..9891f460941c 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -12,7 +12,7 @@ use device::{FrameId, Texture}; #[cfg(feature = "pathfinder")] use euclid::{TypedPoint2D, TypedVector2D}; use gpu_cache::{GpuCache}; -use gpu_types::{BlurDirection, BlurInstance}; +use gpu_types::{BorderInstance, BlurDirection, BlurInstance}; use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator}; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; #[cfg(feature = "pathfinder")] @@ -23,7 +23,7 @@ use profiler::FrameProfileCounters; use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind}; use render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskTree}; use resource_cache::ResourceCache; -use std::{cmp, usize, f32, i32}; +use std::{cmp, usize, f32, i32, mem}; use texture_allocator::GuillotineAllocator; #[cfg(feature = "pathfinder")] use webrender_api::{DevicePixel, FontRenderMode}; @@ -433,6 +433,7 @@ impl RenderTarget for ColorRenderTarget { } } RenderTaskKind::ClipRegion(..) | + RenderTaskKind::Border(..) | RenderTaskKind::CacheMask(..) => { panic!("Should not be added to color target!"); } @@ -565,6 +566,7 @@ impl RenderTarget for AlphaRenderTarget { RenderTaskKind::Readback(..) | RenderTaskKind::Picture(..) | RenderTaskKind::Blit(..) | + RenderTaskKind::Border(..) | RenderTaskKind::Glyph(..) => { panic!("BUG: should not be added to alpha target!"); } @@ -627,6 +629,8 @@ pub struct TextureCacheRenderTarget { pub horizontal_blurs: Vec, pub blits: Vec, pub glyphs: Vec, + pub border_segments: Vec, + pub clears: Vec, } impl TextureCacheRenderTarget { @@ -636,6 +640,8 @@ impl TextureCacheRenderTarget { horizontal_blurs: vec![], blits: vec![], glyphs: vec![], + border_segments: vec![], + clears: vec![], } } @@ -678,6 +684,20 @@ impl TextureCacheRenderTarget { } } } + RenderTaskKind::Border(ref mut task_info) => { + self.clears.push(target_rect.0); + + // TODO(gw): It may be better to store the task origin in + // the render task data instead of per instance. + let task_origin = target_rect.0.origin.to_f32(); + for instance in &mut task_info.instances { + instance.task_origin = task_origin; + } + + let instances = mem::replace(&mut task_info.instances, Vec::new()); + + self.border_segments.extend(instances); + } RenderTaskKind::Glyph(ref mut task_info) => { self.add_glyph_task(task_info, target_rect.0) } diff --git a/gfx/webrender_api/Cargo.toml b/gfx/webrender_api/Cargo.toml index c4011c6059e2..6d33a7b8261d 100644 --- a/gfx/webrender_api/Cargo.toml +++ b/gfx/webrender_api/Cargo.toml @@ -18,8 +18,8 @@ bitflags = "1.0" byteorder = "1.2.1" ipc-channel = {version = "0.10.0", optional = true} euclid = { version = "0.17", features = ["serde"] } -serde = { version = "=1.0.37", features = ["rc"] } -serde_derive = { version = "=1.0.37", features = ["deserialize_in_place"] } +serde = { version = "=1.0.58", features = ["rc"] } +serde_derive = { version = "=1.0.58", features = ["deserialize_in_place"] } serde_bytes = "0.10" time = "0.1" diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index 467ed75ca8da..10cffb78cf8b 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; use std::u32; use {BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DeviceIntPoint, DeviceUintRect}; use {DeviceUintSize, ExternalScrollId, FontInstanceKey, FontInstanceOptions}; -use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphKey, ImageData}; +use {FontInstancePlatformOptions, FontKey, FontVariation, GlyphDimensions, GlyphIndex, ImageData}; use {ImageDescriptor, ImageKey, ItemTag, LayoutPoint, LayoutSize, LayoutTransform, LayoutVector2D}; use {NativeFontHandle, WorldPoint}; @@ -21,12 +21,6 @@ pub type TileSize = u16; /// Documents are rendered in the ascending order of their associated layer values. pub type DocumentLayer = i8; -/// The resource updates for a given transaction (they must be applied in the same frame). -#[derive(Clone, Deserialize, Serialize)] -pub struct ResourceUpdates { - pub updates: Vec, -} - #[derive(Clone, Deserialize, Serialize)] pub enum ResourceUpdate { AddImage(AddImage), @@ -38,94 +32,6 @@ pub enum ResourceUpdate { DeleteFontInstance(FontInstanceKey), } -impl ResourceUpdates { - pub fn new() -> Self { - ResourceUpdates { - updates: Vec::new(), - } - } - - pub fn add_image( - &mut self, - key: ImageKey, - descriptor: ImageDescriptor, - data: ImageData, - tiling: Option, - ) { - self.updates.push(ResourceUpdate::AddImage(AddImage { - key, - descriptor, - data, - tiling, - })); - } - - pub fn update_image( - &mut self, - key: ImageKey, - descriptor: ImageDescriptor, - data: ImageData, - dirty_rect: Option, - ) { - self.updates.push(ResourceUpdate::UpdateImage(UpdateImage { - key, - descriptor, - data, - dirty_rect, - })); - } - - pub fn delete_image(&mut self, key: ImageKey) { - self.updates.push(ResourceUpdate::DeleteImage(key)); - } - - pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec, index: u32) { - self.updates - .push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index))); - } - - pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) { - self.updates - .push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle))); - } - - pub fn delete_font(&mut self, key: FontKey) { - self.updates.push(ResourceUpdate::DeleteFont(key)); - } - - pub fn add_font_instance( - &mut self, - key: FontInstanceKey, - font_key: FontKey, - glyph_size: Au, - options: Option, - platform_options: Option, - variations: Vec, - ) { - self.updates - .push(ResourceUpdate::AddFontInstance(AddFontInstance { - key, - font_key, - glyph_size, - options, - platform_options, - variations, - })); - } - - pub fn delete_font_instance(&mut self, key: FontInstanceKey) { - self.updates.push(ResourceUpdate::DeleteFontInstance(key)); - } - - pub fn merge(&mut self, mut other: ResourceUpdates) { - self.updates.append(&mut other.updates); - } - - pub fn clear(&mut self) { - self.updates.clear() - } -} - /// A Transaction is a group of commands to apply atomically to a document. /// /// This mechanism ensures that: @@ -142,7 +48,7 @@ pub struct Transaction { payloads: Vec, // Resource updates are applied after scene building. - resource_updates: ResourceUpdates, + pub resource_updates: Vec, // If true the transaction is piped through the scene building thread, if false // it will be applied directly on the render backend. @@ -156,7 +62,7 @@ impl Transaction { Transaction { scene_ops: Vec::new(), frame_ops: Vec::new(), - resource_updates: ResourceUpdates::new(), + resource_updates: Vec::new(), payloads: Vec::new(), use_scene_builder_thread: false, // TODO: make this true by default. generate_frame: false, @@ -178,7 +84,7 @@ impl Transaction { !self.generate_frame && self.scene_ops.is_empty() && self.frame_ops.is_empty() && - self.resource_updates.updates.is_empty() + self.resource_updates.is_empty() } pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) { @@ -257,8 +163,8 @@ impl Transaction { self.payloads.push(Payload { epoch, pipeline_id, display_list_data }); } - pub fn update_resources(&mut self, resources: ResourceUpdates) { - self.resource_updates.merge(resources); + pub fn update_resources(&mut self, resources: Vec) { + self.merge(resources); } pub fn set_window_parameters( @@ -353,6 +259,86 @@ impl Transaction { self.payloads, ) } + + pub fn add_image( + &mut self, + key: ImageKey, + descriptor: ImageDescriptor, + data: ImageData, + tiling: Option, + ) { + self.resource_updates.push(ResourceUpdate::AddImage(AddImage { + key, + descriptor, + data, + tiling, + })); + } + + pub fn update_image( + &mut self, + key: ImageKey, + descriptor: ImageDescriptor, + data: ImageData, + dirty_rect: Option, + ) { + self.resource_updates.push(ResourceUpdate::UpdateImage(UpdateImage { + key, + descriptor, + data, + dirty_rect, + })); + } + + pub fn delete_image(&mut self, key: ImageKey) { + self.resource_updates.push(ResourceUpdate::DeleteImage(key)); + } + + pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec, index: u32) { + self.resource_updates + .push(ResourceUpdate::AddFont(AddFont::Raw(key, bytes, index))); + } + + pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) { + self.resource_updates + .push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle))); + } + + pub fn delete_font(&mut self, key: FontKey) { + self.resource_updates.push(ResourceUpdate::DeleteFont(key)); + } + + pub fn add_font_instance( + &mut self, + key: FontInstanceKey, + font_key: FontKey, + glyph_size: Au, + options: Option, + platform_options: Option, + variations: Vec, + ) { + self.resource_updates + .push(ResourceUpdate::AddFontInstance(AddFontInstance { + key, + font_key, + glyph_size, + options, + platform_options, + variations, + })); + } + + pub fn delete_font_instance(&mut self, key: FontInstanceKey) { + self.resource_updates.push(ResourceUpdate::DeleteFontInstance(key)); + } + + pub fn merge(&mut self, mut other: Vec) { + self.resource_updates.append(&mut other); + } + + pub fn clear(&mut self) { + self.resource_updates.clear() + } } /// Represents a transaction in the format sent through the channel. @@ -360,7 +346,7 @@ impl Transaction { pub struct TransactionMsg { pub scene_ops: Vec, pub frame_ops: Vec, - pub resource_updates: ResourceUpdates, + pub resource_updates: Vec, pub generate_frame: bool, pub use_scene_builder_thread: bool, } @@ -370,7 +356,7 @@ impl TransactionMsg { !self.generate_frame && self.scene_ops.is_empty() && self.frame_ops.is_empty() && - self.resource_updates.updates.is_empty() + self.resource_updates.is_empty() } // TODO: We only need this for a few RenderApi methods which we should remove. @@ -378,7 +364,7 @@ impl TransactionMsg { TransactionMsg { scene_ops: Vec::new(), frame_ops: vec![msg], - resource_updates: ResourceUpdates::new(), + resource_updates: Vec::new(), generate_frame: false, use_scene_builder_thread: false, } @@ -388,7 +374,7 @@ impl TransactionMsg { TransactionMsg { scene_ops: vec![msg], frame_ops: Vec::new(), - resource_updates: ResourceUpdates::new(), + resource_updates: Vec::new(), generate_frame: false, use_scene_builder_thread: false, } @@ -598,11 +584,11 @@ pub enum DebugCommand { #[derive(Clone, Deserialize, Serialize)] pub enum ApiMsg { /// Add/remove/update images and fonts. - UpdateResources(ResourceUpdates), + UpdateResources(Vec), /// Gets the glyph dimensions GetGlyphDimensions( FontInstanceKey, - Vec, + Vec, MsgSender>>, ), /// Gets the glyph indices from a string @@ -811,10 +797,10 @@ impl RenderApi { pub fn get_glyph_dimensions( &self, font: FontInstanceKey, - glyph_keys: Vec, + glyph_indices: Vec, ) -> Vec> { let (tx, rx) = channel::msg_channel().unwrap(); - let msg = ApiMsg::GetGlyphDimensions(font, glyph_keys, tx); + let msg = ApiMsg::GetGlyphDimensions(font, glyph_indices, tx); self.api_sender.send(msg).unwrap(); rx.recv().unwrap() } @@ -835,8 +821,8 @@ impl RenderApi { } /// Add/remove/update resources such as images and fonts. - pub fn update_resources(&self, resources: ResourceUpdates) { - if resources.updates.is_empty() { + pub fn update_resources(&self, resources: Vec) { + if resources.is_empty() { return; } self.api_sender diff --git a/gfx/webrender_api/src/display_item.rs b/gfx/webrender_api/src/display_item.rs index 572207c10c42..92d78369b833 100644 --- a/gfx/webrender_api/src/display_item.rs +++ b/gfx/webrender_api/src/display_item.rs @@ -372,7 +372,7 @@ pub struct BorderSide { } #[repr(u32)] -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, Hash, Eq)] pub enum BorderStyle { None = 0, Solid = 1, diff --git a/gfx/webrender_api/src/font.rs b/gfx/webrender_api/src/font.rs index 6bd7ebde498f..035e7d1d4a0b 100644 --- a/gfx/webrender_api/src/font.rs +++ b/gfx/webrender_api/src/font.rs @@ -94,37 +94,7 @@ pub enum FontRenderMode { Subpixel, } -#[repr(u32)] -#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] -pub enum SubpixelDirection { - None = 0, - Horizontal, - Vertical, -} - impl FontRenderMode { - // Skia quantizes subpixel offsets into 1/4 increments. - // Given the absolute position, return the quantized increment - fn subpixel_quantize_offset(&self, pos: f32) -> SubpixelOffset { - // Following the conventions of Gecko and Skia, we want - // to quantize the subpixel position, such that abs(pos) gives: - // [0.0, 0.125) -> Zero - // [0.125, 0.375) -> Quarter - // [0.375, 0.625) -> Half - // [0.625, 0.875) -> ThreeQuarters, - // [0.875, 1.0) -> Zero - // The unit tests below check for this. - let apos = ((pos - pos.floor()) * 8.0) as i32; - - match apos { - 0 | 7 => SubpixelOffset::Zero, - 1...2 => SubpixelOffset::Quarter, - 3...4 => SubpixelOffset::Half, - 5...6 => SubpixelOffset::ThreeQuarters, - _ => unreachable!("bug: unexpected quantized result"), - } - } - // Combine two font render modes such that the lesser amount of AA limits the AA of the result. pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode { match (self, other) { @@ -134,36 +104,6 @@ impl FontRenderMode { } } -impl SubpixelDirection { - // Limit the subpixel direction to what is supported by the render mode. - pub fn limit_by(self, render_mode: FontRenderMode) -> SubpixelDirection { - match render_mode { - FontRenderMode::Mono => SubpixelDirection::None, - FontRenderMode::Alpha | FontRenderMode::Subpixel => self, - } - } -} - -#[repr(u8)] -#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)] -pub enum SubpixelOffset { - Zero = 0, - Quarter = 1, - Half = 2, - ThreeQuarters = 3, -} - -impl Into for SubpixelOffset { - fn into(self) -> f64 { - match self { - SubpixelOffset::Zero => 0.0, - SubpixelOffset::Quarter => 0.25, - SubpixelOffset::Half => 0.5, - SubpixelOffset::ThreeQuarters => 0.75, - } - } -} - #[repr(C)] #[derive(Clone, Copy, Debug, PartialOrd, Deserialize, Serialize)] pub struct FontVariation { @@ -222,6 +162,7 @@ bitflags! { const TRANSPOSE = 1 << 4; const FLIP_X = 1 << 5; const FLIP_Y = 1 << 6; + const SUBPIXEL_POSITION = 1 << 7; // Windows flags const FORCE_GDI = 1 << 16; @@ -233,23 +174,25 @@ bitflags! { const FORCE_AUTOHINT = 1 << 16; const NO_AUTOHINT = 1 << 17; const VERTICAL_LAYOUT = 1 << 18; + const LCD_VERTICAL = 1 << 19; } } impl Default for FontInstanceFlags { #[cfg(target_os = "windows")] fn default() -> FontInstanceFlags { - FontInstanceFlags::empty() + FontInstanceFlags::SUBPIXEL_POSITION } #[cfg(target_os = "macos")] fn default() -> FontInstanceFlags { + FontInstanceFlags::SUBPIXEL_POSITION | FontInstanceFlags::FONT_SMOOTHING } #[cfg(not(any(target_os = "macos", target_os = "windows")))] fn default() -> FontInstanceFlags { - FontInstanceFlags::empty() + FontInstanceFlags::SUBPIXEL_POSITION } } @@ -258,7 +201,6 @@ impl Default for FontInstanceFlags { #[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct FontInstanceOptions { pub render_mode: FontRenderMode, - pub subpx_dir: SubpixelDirection, pub flags: FontInstanceFlags, /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel, /// the text will be rendered with bg_color.r/g/b as an opaque estimated @@ -270,7 +212,6 @@ impl Default for FontInstanceOptions { fn default() -> FontInstanceOptions { FontInstanceOptions { render_mode: FontRenderMode::Subpixel, - subpx_dir: SubpixelDirection::Horizontal, flags: Default::default(), bg_color: ColorU::new(0, 0, 0, 0), } @@ -358,32 +299,6 @@ impl FontInstanceKey { } } -#[derive(Clone, Hash, PartialEq, Eq, Debug, Deserialize, Serialize, Ord, PartialOrd)] -pub struct GlyphKey { - pub index: u32, - pub subpixel_offset: SubpixelOffset, -} - -impl GlyphKey { - pub fn new( - index: u32, - point: LayoutPoint, - render_mode: FontRenderMode, - subpx_dir: SubpixelDirection, - ) -> GlyphKey { - let pos = match subpx_dir { - SubpixelDirection::None => 0.0, - SubpixelDirection::Horizontal => point.x, - SubpixelDirection::Vertical => point.y, - }; - - GlyphKey { - index, - subpixel_offset: render_mode.subpixel_quantize_offset(pos), - } - } -} - pub type GlyphIndex = u32; #[repr(C)] @@ -393,52 +308,3 @@ pub struct GlyphInstance { pub point: LayoutPoint, } -#[cfg(test)] -mod test { - use super::{FontRenderMode, SubpixelOffset}; - - #[test] - fn test_subpx_quantize() { - let rm = FontRenderMode::Subpixel; - - assert_eq!(rm.subpixel_quantize_offset(0.0), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(-0.0), SubpixelOffset::Zero); - - assert_eq!(rm.subpixel_quantize_offset(0.1), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.01), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.05), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.12), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.124), SubpixelOffset::Zero); - - assert_eq!(rm.subpixel_quantize_offset(0.125), SubpixelOffset::Quarter); - assert_eq!(rm.subpixel_quantize_offset(0.2), SubpixelOffset::Quarter); - assert_eq!(rm.subpixel_quantize_offset(0.25), SubpixelOffset::Quarter); - assert_eq!(rm.subpixel_quantize_offset(0.33), SubpixelOffset::Quarter); - assert_eq!(rm.subpixel_quantize_offset(0.374), SubpixelOffset::Quarter); - - assert_eq!(rm.subpixel_quantize_offset(0.375), SubpixelOffset::Half); - assert_eq!(rm.subpixel_quantize_offset(0.4), SubpixelOffset::Half); - assert_eq!(rm.subpixel_quantize_offset(0.5), SubpixelOffset::Half); - assert_eq!(rm.subpixel_quantize_offset(0.58), SubpixelOffset::Half); - assert_eq!(rm.subpixel_quantize_offset(0.624), SubpixelOffset::Half); - - assert_eq!(rm.subpixel_quantize_offset(0.625), SubpixelOffset::ThreeQuarters); - assert_eq!(rm.subpixel_quantize_offset(0.67), SubpixelOffset::ThreeQuarters); - assert_eq!(rm.subpixel_quantize_offset(0.7), SubpixelOffset::ThreeQuarters); - assert_eq!(rm.subpixel_quantize_offset(0.78), SubpixelOffset::ThreeQuarters); - assert_eq!(rm.subpixel_quantize_offset(0.874), SubpixelOffset::ThreeQuarters); - - assert_eq!(rm.subpixel_quantize_offset(0.875), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.89), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.91), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.967), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(0.999), SubpixelOffset::Zero); - - assert_eq!(rm.subpixel_quantize_offset(-1.0), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(1.0), SubpixelOffset::Zero); - assert_eq!(rm.subpixel_quantize_offset(1.5), SubpixelOffset::Half); - assert_eq!(rm.subpixel_quantize_offset(-1.625), SubpixelOffset::Half); - assert_eq!(rm.subpixel_quantize_offset(-4.33), SubpixelOffset::ThreeQuarters); - - } -} diff --git a/gfx/webrender_api/src/image.rs b/gfx/webrender_api/src/image.rs index d7dfb5a9e73d..ac6cc271b09e 100644 --- a/gfx/webrender_api/src/image.rs +++ b/gfx/webrender_api/src/image.rs @@ -6,7 +6,8 @@ extern crate serde_bytes; use font::{FontInstanceKey, FontKey, FontTemplate}; use std::sync::Arc; -use {DevicePoint, DeviceUintRect, DeviceUintSize, IdNamespace, TileOffset, TileSize}; +use {DevicePoint, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; +use {IdNamespace, TileOffset, TileSize}; use euclid::size2; #[repr(C)] @@ -106,6 +107,13 @@ impl ImageDescriptor { pub fn compute_total_size(&self) -> u32 { self.compute_stride() * self.size.height } + + pub fn full_rect(&self) -> DeviceUintRect { + DeviceUintRect::new( + DeviceUintPoint::zero(), + self.size, + ) + } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/gfx/webrender_api/src/units.rs b/gfx/webrender_api/src/units.rs index a1946f4a6ccb..7ea431f5d23d 100644 --- a/gfx/webrender_api/src/units.rs +++ b/gfx/webrender_api/src/units.rs @@ -92,6 +92,8 @@ pub type DevicePixelScale = TypedScale; /// Scaling ratio from layout to world. Used for cases where we know the layout /// is in world space, or specifically want to treat it this way. pub type LayoutToWorldScale = TypedScale; +/// A complete scaling ratio from layout space to device pixel space. +pub type LayoutToDeviceScale = TypedScale; pub type LayoutTransform = TypedTransform3D; pub type LayoutToScrollTransform = TypedTransform3D; diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index 914ff469be6c..1da4625b4373 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -bb354abbf84602d3d8357c63c4f0b1139ec4deb1 +63c71ca9bbe4dec0ebc9c9bc8ab65b06a6b40641 diff --git a/gfx/wrench/src/json_frame_writer.rs b/gfx/wrench/src/json_frame_writer.rs index f8002cfb03e4..cb16648ad711 100644 --- a/gfx/wrench/src/json_frame_writer.rs +++ b/gfx/wrench/src/json_frame_writer.rs @@ -111,8 +111,8 @@ impl JsonFrameWriter { file.write_all(b"\n").unwrap(); } - fn update_resources(&mut self, updates: &ResourceUpdates) { - for update in &updates.updates { + fn update_resources(&mut self, updates: &[ResourceUpdate]) { + for update in updates { match *update { ResourceUpdate::AddImage(ref img) => { let stride = img.descriptor.stride.unwrap_or( diff --git a/gfx/wrench/src/rawtest.rs b/gfx/wrench/src/rawtest.rs index 2aee7bf41589..15e38af28cbc 100644 --- a/gfx/wrench/src/rawtest.rs +++ b/gfx/wrench/src/rawtest.rs @@ -30,7 +30,9 @@ fn rect(x: T, y: T, width: T, height: T) -> TypedRect { } impl<'a> RawtestHarness<'a> { - pub fn new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: &'a Receiver) -> Self { + pub fn new(wrench: &'a mut Wrench, + window: &'a mut WindowWrapper, + rx: &'a Receiver) -> Self { RawtestHarness { wrench, rx, @@ -45,6 +47,7 @@ impl<'a> RawtestHarness<'a> { self.test_blob_update_epoch_test(); self.test_tile_decomposition(); self.test_very_large_blob(); + self.test_offscreen_blob(); self.test_save_restore(); self.test_capture(); self.test_zero_height_window(); @@ -61,13 +64,13 @@ impl<'a> RawtestHarness<'a> { epoch: &mut Epoch, layout_size: LayoutSize, builder: DisplayListBuilder, - resources: Option + resources: &[ResourceUpdate] ) { let mut txn = Transaction::new(); let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0)); txn.use_scene_builder_thread(); - if let Some(resources) = resources { - txn.update_resources(resources); + if !resources.is_empty() { + txn.resource_updates = resources.to_vec(); } txn.set_display_list( *epoch, @@ -82,14 +85,15 @@ impl<'a> RawtestHarness<'a> { self.wrench.api.send_transaction(self.wrench.document_id, txn); } + fn test_tile_decomposition(&mut self) { println!("\ttile decomposition..."); // This exposes a crash in tile decomposition let layout_size = LayoutSize::new(800., 800.); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); let blob_img = self.wrench.api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img, ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), @@ -112,16 +116,16 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(0); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); self.rx.recv().unwrap(); self.wrench.render(); // Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this - resources = ResourceUpdates::new(); - resources.delete_image(blob_img); - self.wrench.api.update_resources(resources); + txn = Transaction::new(); + txn.delete_image(blob_img); + self.wrench.api.update_resources(txn.resource_updates); } fn test_very_large_blob(&mut self) { @@ -140,10 +144,10 @@ impl<'a> RawtestHarness<'a> { // This exposes a crash in tile decomposition let layout_size = LayoutSize::new(800., 800.); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); let blob_img = self.wrench.api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img, ImageDescriptor::new(1510, 111256, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), @@ -172,7 +176,7 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(0); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let called = Arc::new(AtomicIsize::new(0)); let called_inner = Arc::clone(&called); @@ -188,23 +192,149 @@ impl<'a> RawtestHarness<'a> { // make sure things are in the right spot assert!( - pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4] == 255 && - pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 1] == 255 && - pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 2] == 255 && - pixels[(148 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 3] == 255 + pixels[(148 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4] == 255 && + pixels[(148 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4 + 1] == 255 && + pixels[(148 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4 + 2] == 255 && + pixels[(148 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4 + 3] == 255 ); assert!( - pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4] == 50 && - pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 1] == 50 && - pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 2] == 150 && - pixels[(132 + (window_rect.size.height as usize - 148) * window_rect.size.width as usize) * 4 + 3] == 255 + pixels[(132 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4] == 50 && + pixels[(132 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4 + 1] == 50 && + pixels[(132 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4 + 2] == 150 && + pixels[(132 + + (window_rect.size.height as usize - 148) * + window_rect.size.width as usize) * 4 + 3] == 255 ); // Leaving a tiled blob image in the resource cache // confuses the `test_capture`. TODO: remove this - resources = ResourceUpdates::new(); - resources.delete_image(blob_img); - self.wrench.api.update_resources(resources); + txn = Transaction::new(); + txn.delete_image(blob_img); + self.wrench.api.update_resources(txn.resource_updates); + } + + fn test_offscreen_blob(&mut self) { + println!("\toffscreen blob update."); + + assert_eq!(self.wrench.device_pixel_ratio, 1.); + + let window_size = self.window.get_inner_size(); + + let test_size = DeviceUintSize::new(800, 800); + + let window_rect = DeviceUintRect::new( + DeviceUintPoint::new(0, window_size.height - test_size.height), + test_size, + ); + + // This exposes a crash in tile decomposition + let mut txn = Transaction::new(); + let layout_size = LayoutSize::new(800., 800.); + + let blob_img = self.wrench.api.generate_image_key(); + txn.add_image( + blob_img, + ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false), + ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + None, + ); + + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); + + let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.)); + + let image_size = size(1510., 1510.); + + // setup some malicious image size parameters + builder.push_image( + &info, + image_size, + image_size, + ImageRendering::Auto, + AlphaType::PremultipliedAlpha, + blob_img, + ); + + let mut epoch = Epoch(0); + + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); + + let original_pixels = self.render_and_get_pixels(window_rect); + + let mut epoch = Epoch(1); + + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); + + let info = LayoutPrimitiveInfo::new(rect(-10000., 0.0, 1510., 1510.)); + + let image_size = size(1510., 1510.); + + // setup some malicious image size parameters + builder.push_image( + &info, + image_size, + image_size, + ImageRendering::Auto, + AlphaType::PremultipliedAlpha, + blob_img, + ); + + self.submit_dl(&mut epoch, layout_size, builder, &[]); + + let _offscreen_pixels = self.render_and_get_pixels(window_rect); + + let mut txn = Transaction::new(); + + txn.update_image( + blob_img, + ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, false, false), + ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), + Some(rect(10, 10, 100, 100)), + ); + + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); + + let info = LayoutPrimitiveInfo::new(rect(0., 0.0, 1510., 1510.)); + + let image_size = size(1510., 1510.); + + // setup some malicious image size parameters + builder.push_image( + &info, + image_size, + image_size, + ImageRendering::Auto, + AlphaType::PremultipliedAlpha, + blob_img, + ); + + let mut epoch = Epoch(2); + + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); + + let pixels = self.render_and_get_pixels(window_rect); + + assert!(pixels == original_pixels); + + // Leaving a tiled blob image in the resource cache + // confuses the `test_capture`. TODO: remove this + txn = Transaction::new(); + txn.delete_image(blob_img); + self.wrench.api.update_resources(txn.resource_updates); } fn test_retained_blob_images_test(&mut self) { @@ -219,12 +349,12 @@ impl<'a> RawtestHarness<'a> { test_size, ); let layout_size = LayoutSize::new(400., 400.); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); { let api = &self.wrench.api; blob_img = api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), @@ -247,7 +377,7 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(0); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let called = Arc::new(AtomicIsize::new(0)); let called_inner = Arc::clone(&called); @@ -274,7 +404,9 @@ impl<'a> RawtestHarness<'a> { blob_img, ); - self.submit_dl(&mut epoch, layout_size, builder, None); + txn.resource_updates.clear(); + + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_second = self.render_and_get_pixels(window_rect); @@ -300,19 +432,19 @@ impl<'a> RawtestHarness<'a> { test_size, ); let layout_size = LayoutSize::new(400., 400.); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); let (blob_img, blob_img2) = { let api = &self.wrench.api; blob_img = api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), None, ); blob_img2 = api.generate_image_key(); - resources.add_image( + txn.add_image( blob_img2, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(80, 50, 150, 255))), @@ -364,19 +496,19 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(0); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let _pixels_first = self.render_and_get_pixels(window_rect); // update and redraw both images - let mut resources = ResourceUpdates::new(); - resources.update_image( + let mut txn = Transaction::new(); + txn.update_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), Some(rect(100, 100, 100, 100)), ); - resources.update_image( + txn.update_image( blob_img2, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(59, 50, 150, 255))), @@ -385,13 +517,13 @@ impl<'a> RawtestHarness<'a> { let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); push_images(&mut builder); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let _pixels_second = self.render_and_get_pixels(window_rect); // only update the first image - let mut resources = ResourceUpdates::new(); - resources.update_image( + let mut txn = Transaction::new(); + txn.update_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))), @@ -400,7 +532,7 @@ impl<'a> RawtestHarness<'a> { let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); push_images(&mut builder); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let _pixels_third = self.render_and_get_pixels(window_rect); // the first image should be requested 3 times @@ -420,11 +552,11 @@ impl<'a> RawtestHarness<'a> { test_size, ); let layout_size = LayoutSize::new(400., 400.); - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); let blob_img = { let img = self.wrench.api.generate_image_key(); - resources.add_image( + txn.add_image( img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), @@ -448,12 +580,12 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(0); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_first = self.render_and_get_pixels(window_rect); // draw the blob image a second time after updating it with the same color - let mut resources = ResourceUpdates::new(); - resources.update_image( + let mut txn = Transaction::new(); + txn.update_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))), @@ -472,12 +604,12 @@ impl<'a> RawtestHarness<'a> { blob_img, ); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_second = self.render_and_get_pixels(window_rect); // draw the blob image a third time after updating it with a different color - let mut resources = ResourceUpdates::new(); - resources.update_image( + let mut txn = Transaction::new(); + txn.update_image( blob_img, ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false), ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))), @@ -496,7 +628,7 @@ impl<'a> RawtestHarness<'a> { blob_img, ); - self.submit_dl(&mut epoch, layout_size, builder, Some(resources)); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); let pixels_third = self.render_and_get_pixels(window_rect); assert!(pixels_first == pixels_second); @@ -537,13 +669,13 @@ impl<'a> RawtestHarness<'a> { ); builder.push_clip_id(clip); builder.push_rect(&PrimitiveInfo::new(rect(110., 110., 50., 50.)), - ColorF::new(0.0, 1.0, 0.0, 1.0)); + ColorF::new(0.0, 1.0, 0.0, 1.0)); builder.push_shadow(&PrimitiveInfo::new(rect(100., 100., 100., 100.)), - Shadow { - offset: LayoutVector2D::new(1.0, 1.0), - blur_radius: 1.0, - color: ColorF::new(0.0, 0.0, 0.0, 1.0), - }); + Shadow { + offset: LayoutVector2D::new(1.0, 1.0), + blur_radius: 1.0, + color: ColorF::new(0.0, 0.0, 0.0, 1.0), + }); builder.push_line(&PrimitiveInfo::new(rect(110., 110., 50., 2.)), 0.0, LineOrientation::Horizontal, &ColorF::new(0.0, 0.0, 0.0, 1.0), LineStyle::Solid); @@ -567,7 +699,9 @@ impl<'a> RawtestHarness<'a> { builder.pop_clip_id(); - self.submit_dl(&mut Epoch(0), layout_size, builder, None); + let txn = Transaction::new(); + + self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates); self.render_and_get_pixels(window_rect) }; @@ -590,9 +724,9 @@ impl<'a> RawtestHarness<'a> { // 1. render some scene - let mut resources = ResourceUpdates::new(); + let mut txn = Transaction::new(); let image = self.wrench.api.generate_image_key(); - resources.add_image( + txn.add_image( image, ImageDescriptor::new(1, 1, ImageFormat::BGRA8, true, false), ImageData::new(vec![0xFF, 0, 0, 0xFF]), @@ -668,7 +802,8 @@ impl<'a> RawtestHarness<'a> { let doc_id = self.wrench.api.add_document(window_size, 1); let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); - let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0))); + let info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), + LayoutSize::new(100.0, 100.0))); builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0)); let mut txn = Transaction::new(); @@ -709,7 +844,7 @@ impl<'a> RawtestHarness<'a> { info.tag = Some((0, 2)); builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0)); - let make_rounded_complex_clip = | rect: &LayoutRect, radius: f32 | -> ComplexClipRegion { + let make_rounded_complex_clip = |rect: &LayoutRect, radius: f32| -> ComplexClipRegion { ComplexClipRegion::new( *rect, BorderRadius::uniform_size(LayoutSize::new(radius, radius)), @@ -743,13 +878,14 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(0); - self.submit_dl(&mut epoch, layout_size, builder, None); + let txn = Transaction::new(); + self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates); // We render to ensure that the hit tester is up to date with the current scene. self.rx.recv().unwrap(); self.wrench.render(); - let hit_test = | point: WorldPoint | -> HitTestResult { + let hit_test = |point: WorldPoint| -> HitTestResult { self.wrench.api.hit_test( self.wrench.document_id, None, @@ -758,7 +894,7 @@ impl<'a> RawtestHarness<'a> { ) }; - let assert_hit_test = | point: WorldPoint, tags: Vec | { + let assert_hit_test = |point: WorldPoint, tags: Vec| { let result = hit_test(point); assert_eq!(result.items.len(), tags.len()); @@ -779,7 +915,7 @@ impl<'a> RawtestHarness<'a> { // The middle of the normal rectangle should be hit. assert_hit_test(WorldPoint::new(150., 50.), vec![(0, 2), (0, 1)]); - let test_rounded_rectangle = | point: WorldPoint, size: WorldSize, tag: ItemTag | { + let test_rounded_rectangle = |point: WorldPoint, size: WorldSize, tag: ItemTag| { // The cut out corners of the rounded rectangle should not be hit. let top_left = point + WorldVector2D::new(5., 5.); let bottom_right = point + size.to_vector() - WorldVector2D::new(5., 5.); diff --git a/gfx/wrench/src/ron_frame_writer.rs b/gfx/wrench/src/ron_frame_writer.rs index 1f493c7c2d0b..82c38b339763 100644 --- a/gfx/wrench/src/ron_frame_writer.rs +++ b/gfx/wrench/src/ron_frame_writer.rs @@ -87,8 +87,8 @@ impl RonFrameWriter { file.write_all(b"\n").unwrap(); } - fn update_resources(&mut self, updates: &ResourceUpdates) { - for update in &updates.updates { + fn update_resources(&mut self, updates: &[ResourceUpdate]) { + for update in updates { match *update { ResourceUpdate::AddImage(ref img) => { let bytes = match img.data { diff --git a/gfx/wrench/src/wrench.rs b/gfx/wrench/src/wrench.rs index 0b95a179af19..21026d11bfb2 100644 --- a/gfx/wrench/src/wrench.rs +++ b/gfx/wrench/src/wrench.rs @@ -280,7 +280,6 @@ impl Wrench { &mut self, font_key: FontKey, instance_key: FontInstanceKey, - render_mode: Option, text: &str, size: Au, origin: LayoutPoint, @@ -294,20 +293,8 @@ impl Wrench { .filter_map(|idx| *idx) .collect(); - let render_mode = render_mode.unwrap_or(::default().render_mode); - let subpx_dir = SubpixelDirection::Horizontal.limit_by(render_mode); - // Retrieve the metrics for each glyph. - let mut keys = Vec::new(); - for glyph_index in &indices { - keys.push(GlyphKey::new( - *glyph_index, - LayoutPoint::zero(), - render_mode, - subpx_dir, - )); - } - let metrics = self.api.get_glyph_dimensions(instance_key, keys); + let metrics = self.api.get_glyph_dimensions(instance_key, indices.clone()); let mut bounding_rect = LayoutRect::zero(); let mut positions = Vec::new(); @@ -385,9 +372,9 @@ impl Wrench { #[cfg(target_os = "windows")] pub fn font_key_from_native_handle(&mut self, descriptor: &NativeFontHandle) -> FontKey { let key = self.api.generate_font_key(); - let mut resources = ResourceUpdates::new(); - resources.add_native_font(key, descriptor.clone()); - self.api.update_resources(resources); + let mut txn = Transaction::new(); + txn.add_native_font(key, descriptor.clone()); + self.api.update_resources(txn.resource_updates); key } @@ -456,9 +443,9 @@ impl Wrench { pub fn font_key_from_bytes(&mut self, bytes: Vec, index: u32) -> FontKey { let key = self.api.generate_font_key(); - let mut update = ResourceUpdates::new(); - update.add_raw_font(key, bytes, index); - self.api.update_resources(update); + let mut txn = Transaction::new(); + txn.add_raw_font(key, bytes, index); + self.api.update_resources(txn.resource_updates); key } @@ -470,7 +457,7 @@ impl Wrench { bg_color: Option, ) -> FontInstanceKey { let key = self.api.generate_font_instance_key(); - let mut update = ResourceUpdates::new(); + let mut txn = Transaction::new(); let mut options: FontInstanceOptions = Default::default(); options.flags |= flags; if let Some(render_mode) = render_mode { @@ -479,16 +466,16 @@ impl Wrench { if let Some(bg_color) = bg_color { options.bg_color = bg_color; } - update.add_font_instance(key, font_key, size, Some(options), None, Vec::new()); - self.api.update_resources(update); + txn.add_font_instance(key, font_key, size, Some(options), None, Vec::new()); + self.api.update_resources(txn.resource_updates); key } #[allow(dead_code)] pub fn delete_font_instance(&mut self, key: FontInstanceKey) { - let mut update = ResourceUpdates::new(); - update.delete_font_instance(key); - self.api.update_resources(update); + let mut txn = Transaction::new(); + txn.delete_font_instance(key); + self.api.update_resources(txn.resource_updates); } pub fn update(&mut self, dim: DeviceUintSize) { diff --git a/gfx/wrench/src/yaml_frame_reader.rs b/gfx/wrench/src/yaml_frame_reader.rs index 1ce3f0860cc2..a0e2c44c9bce 100644 --- a/gfx/wrench/src/yaml_frame_reader.rs +++ b/gfx/wrench/src/yaml_frame_reader.rs @@ -230,17 +230,17 @@ impl YamlFrameReader { } pub fn deinit(mut self, wrench: &mut Wrench) { - let mut updates = ResourceUpdates::new(); + let mut txn = Transaction::new(); for (_, font_instance) in self.font_instances.drain() { - updates.delete_font_instance(font_instance); + txn.delete_font_instance(font_instance); } for (_, font) in self.fonts.drain() { - updates.delete_font(font); + txn.delete_font(font); } - wrench.api.update_resources(updates); + wrench.api.update_resources(txn.resource_updates); } pub fn yaml_path(&self) -> &PathBuf { @@ -511,9 +511,9 @@ impl YamlFrameReader { }; let tiling = tiling.map(|tile_size| tile_size as u16); let image_key = wrench.api.generate_image_key(); - let mut resources = ResourceUpdates::new(); - resources.add_image(image_key, descriptor, image_data, tiling); - wrench.api.update_resources(resources); + let mut txn = Transaction::new(); + txn.add_image(image_key, descriptor, image_data, tiling); + wrench.api.update_resources(txn.resource_updates); let val = ( image_key, LayoutSize::new(descriptor.size.width as f32, descriptor.size.height as f32), @@ -1210,7 +1210,6 @@ impl YamlFrameReader { let (glyph_indices, glyph_positions, bounds) = wrench.layout_simple_ascii( font_key, font_instance_key, - self.font_render_mode, text, size, origin, diff --git a/gfx/wrench/src/yaml_frame_writer.rs b/gfx/wrench/src/yaml_frame_writer.rs index 57e2fd356852..4014fc04deb5 100644 --- a/gfx/wrench/src/yaml_frame_writer.rs +++ b/gfx/wrench/src/yaml_frame_writer.rs @@ -503,8 +503,8 @@ impl YamlFrameWriter { scene.finish_display_list(self.pipeline_id.unwrap(), dl); } - fn update_resources(&mut self, updates: &ResourceUpdates) { - for update in &updates.updates { + fn update_resources(&mut self, updates: &[ResourceUpdate]) { + for update in updates { match *update { ResourceUpdate::AddImage(ref img) => { if let Some(ref data) = self.images.get(&img.key) { @@ -545,7 +545,8 @@ impl YamlFrameWriter { data.path = None; data.bytes = Some((**bytes).clone()); } else { - // Other existing image types only make sense within the gecko integration. + // Other existing image types only make sense + // within the gecko integration. println!( "Wrench only supports updating buffer images ({}).", "ignoring update command"