--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -11,18 +11,18 @@ default = ["freetype-lib"]
freetype-lib = ["freetype/servo-freetype-sys"]
profiler = ["thread_profiler/thread_profiler"]
debugger = ["ws", "serde_json", "serde", "image", "base64"]
capture = ["webrender_api/serialize", "ron", "serde"]
replay = ["webrender_api/deserialize", "ron", "serde"]
[dependencies]
app_units = "0.6"
-bincode = "0.9"
byteorder = "1.0"
+bincode = "1.0"
euclid = "0.17"
fxhash = "0.2.1"
gleam = "0.4.20"
lazy_static = "1"
log = "0.4"
num-traits = "0.1.32"
time = "0.1"
rayon = "1"
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -197,17 +197,17 @@ impl Example for App {
None,
MixBlendMode::Normal,
Vec::new(),
);
let image_mask_key = api.generate_image_key();
resources.add_image(
image_mask_key,
- ImageDescriptor::new(2, 2, ImageFormat::R8, true),
+ ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
ImageData::new(vec![0, 80, 180, 255]),
None,
);
let mask = ImageMask {
image: image_mask_key,
rect: (75, 75).by(100, 100),
repeat: false,
};
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -227,25 +227,25 @@ impl Example for App {
resources: &mut ResourceUpdates,
_framebuffer_size: api::DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let blob_img1 = api.generate_image_key();
resources.add_image(
blob_img1,
- api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
+ api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
Some(128),
);
let blob_img2 = api.generate_image_key();
resources.add_image(
blob_img2,
- api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
+ api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
let info = api::LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -133,17 +133,16 @@ pub fn main_wrapper<E: Example>(
println!("OpenGL version {}", gl.get_string(gl::VERSION));
println!("Shader resource path: {:?}", res_path);
let device_pixel_ratio = window.hidpi_factor();
println!("Device pixel ratio: {}", device_pixel_ratio);
println!("Loading shaders...");
let opts = webrender::RendererOptions {
resource_override_path: res_path,
- debug: true,
precache_shaders: E::PRECACHE_SHADERS,
device_pixel_ratio,
clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
//scatter_gpu_cache_updates: false,
..options.unwrap_or(webrender::RendererOptions::default())
};
let framebuffer_size = {
--- a/gfx/webrender/examples/common/image_helper.rs
+++ b/gfx/webrender/examples/common/image_helper.rs
@@ -7,10 +7,10 @@ use webrender::api::{ImageData, ImageDes
pub fn make_checkerboard(width: u32, height: u32) -> (ImageDescriptor, ImageData) {
let mut image_data = Vec::new();
for y in 0 .. height {
for x in 0 .. width {
let lum = 255 * (((x & 8) == 0) ^ ((y & 8) == 0)) as u8;
image_data.extend_from_slice(&[lum, lum, lum, 0xff]);
}
}
- (ImageDescriptor::new(width, height, ImageFormat::BGRA8, true), ImageData::new(image_data))
+ (ImageDescriptor::new(width, height, ImageFormat::BGRA8, true, false), ImageData::new(image_data))
}
--- a/gfx/webrender/examples/frame_output.rs
+++ b/gfx/webrender/examples/frame_output.rs
@@ -65,17 +65,17 @@ impl App {
framebuffer_size: DeviceUintSize,
device_pixel_ratio: f32,
) {
// 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(
self.external_image_key.unwrap(),
- ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
}),
None,
);
--- a/gfx/webrender/examples/image_resize.rs
+++ b/gfx/webrender/examples/image_resize.rs
@@ -96,17 +96,17 @@ impl Example for App {
let g = 255 * ((x & 32) == 0) as u8;
image_data.extend_from_slice(&[0, g, r, 0xff]);
}
}
let mut updates = ResourceUpdates::new();
updates.update_image(
self.image_key,
- ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true),
+ 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);
}
--- a/gfx/webrender/examples/multiwindow.rs
+++ b/gfx/webrender/examples/multiwindow.rs
@@ -81,17 +81,16 @@ impl Window {
gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
},
glutin::Api::WebGl => unimplemented!(),
};
let device_pixel_ratio = window.hidpi_factor();
let opts = webrender::RendererOptions {
- debug: true,
device_pixel_ratio,
clear_color: Some(clear_color),
..webrender::RendererOptions::default()
};
let framebuffer_size = {
let (width, height) = window.get_inner_size().unwrap();
DeviceUintSize::new(width, height)
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ b/gfx/webrender/examples/texture_cache_stress.rs
@@ -106,25 +106,25 @@ impl Example for App {
if self.swap_keys.is_empty() {
let key0 = api.generate_image_key();
let key1 = api.generate_image_key();
self.image_generator.generate_image(128);
resources.add_image(
key0,
- ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.image_generator.generate_image(128);
resources.add_image(
key1,
- ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.swap_keys.push(key0);
self.swap_keys.push(key1);
}
@@ -210,17 +210,17 @@ impl Example for App {
let size = 4;
let image_key = api.generate_image_key();
self.image_generator.generate_image(size);
updates.add_image(
image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.stress_keys.push(image_key);
}
}
}
@@ -228,17 +228,17 @@ impl Example for App {
updates.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(
image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
},
glutin::VirtualKeyCode::E => {
if let Some(image_key) = self.image_key.take() {
updates.delete_image(image_key);
}
@@ -249,17 +249,17 @@ impl Example for App {
let image_data = ExternalImageData {
id: ExternalImageId(0),
channel_index: size as u8,
image_type: ExternalImageType::Buffer,
};
updates.add_image(
image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::External(image_data),
None,
);
self.image_key = Some(image_key);
}
glutin::VirtualKeyCode::R => {
if let Some(image_key) = self.image_key.take() {
@@ -267,17 +267,17 @@ impl Example for App {
}
let image_key = api.generate_image_key();
let size = 32;
self.image_generator.generate_image(size);
updates.add_image(
image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
ImageData::new(self.image_generator.take()),
None,
);
self.image_key = Some(image_key);
}
_ => {}
}
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -96,53 +96,53 @@ impl Example for App {
);
let yuv_chanel1 = api.generate_image_key();
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(
yuv_chanel1,
- ImageDescriptor::new(100, 100, ImageFormat::R8, true),
+ ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(0),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
resources.add_image(
yuv_chanel2,
- ImageDescriptor::new(100, 100, ImageFormat::RG8, true),
+ ImageDescriptor::new(100, 100, ImageFormat::RG8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(1),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
resources.add_image(
yuv_chanel2_1,
- ImageDescriptor::new(100, 100, ImageFormat::R8, true),
+ ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(2),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
);
resources.add_image(
yuv_chanel3,
- ImageDescriptor::new(100, 100, ImageFormat::R8, true),
+ ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
ImageData::External(ExternalImageData {
id: ExternalImageId(3),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(
TextureTarget::Default,
),
}),
None,
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -84,94 +84,66 @@ void main(void) {
RectWithSize local_segment_rect = RectWithSize(segment_data[0].xy, segment_data[0].zw);
VertexInfo vi;
// Fetch the dynamic picture that we are drawing on.
PictureTask pic_task = fetch_picture_task(brush.picture_address);
ClipArea clip_area = fetch_clip_area(brush.clip_address);
- if (pic_task.pic_kind_and_raster_mode > 0.0) {
- vec2 local_pos = local_segment_rect.p0 + aPosition.xy * local_segment_rect.size;
- vec2 clamped_local_pos = clamp_rect(local_pos, brush_prim.local_clip_rect);
-
- vec2 device_pos = uDevicePixelRatio * clamped_local_pos;
-
- vec2 final_pos = device_pos +
- pic_task.common_data.task_rect.p0 -
- uDevicePixelRatio * pic_task.content_origin;
+ ClipScrollNode scroll_node = fetch_clip_scroll_node(brush.scroll_node_id);
-#ifdef WR_FEATURE_ALPHA_PASS
- write_clip(
- vec2(0.0),
- clip_area
- );
-#endif
-
- vi = VertexInfo(
- local_pos,
- device_pos,
- 1.0,
- device_pos
+ // Write the normal vertex information out.
+ if (scroll_node.is_axis_aligned) {
+ vi = write_vertex(
+ local_segment_rect,
+ brush_prim.local_clip_rect,
+ float(brush.z),
+ scroll_node,
+ pic_task,
+ brush_prim.local_rect
);
- // Write the final position transformed by the orthographic device-pixel projection.
- gl_Position = uTransform * vec4(final_pos, 0.0, 1.0);
- } else {
- ClipScrollNode scroll_node = fetch_clip_scroll_node(brush.scroll_node_id);
-
- // Write the normal vertex information out.
- if (scroll_node.is_axis_aligned) {
- vi = write_vertex(
- local_segment_rect,
- brush_prim.local_clip_rect,
- float(brush.z),
- scroll_node,
- pic_task,
- brush_prim.local_rect
- );
-
- // TODO(gw): vLocalBounds may be referenced by
- // the fragment shader when running in
- // the alpha pass, even on non-transformed
- // items. For now, just ensure it has no
- // effect. We can tidy this up as we move
- // more items to be brush shaders.
+ // TODO(gw): vLocalBounds may be referenced by
+ // the fragment shader when running in
+ // the alpha pass, even on non-transformed
+ // items. For now, just ensure it has no
+ // effect. We can tidy this up as we move
+ // more items to be brush shaders.
#ifdef WR_FEATURE_ALPHA_PASS
- vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0));
+ vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0));
#endif
- } else {
- bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0));
- bool do_perspective_interpolation = (brush.flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0;
+ } else {
+ bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0));
+ bool do_perspective_interpolation = (brush.flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0;
- vi = write_transform_vertex(
- local_segment_rect,
- brush_prim.local_rect,
- brush_prim.local_clip_rect,
- mix(vec4(0.0), vec4(1.0), edge_mask),
- float(brush.z),
- scroll_node,
- pic_task,
- do_perspective_interpolation
- );
- }
+ vi = write_transform_vertex(
+ local_segment_rect,
+ brush_prim.local_rect,
+ brush_prim.local_clip_rect,
+ mix(vec4(0.0), vec4(1.0), edge_mask),
+ float(brush.z),
+ scroll_node,
+ pic_task,
+ do_perspective_interpolation
+ );
+ }
- // For brush instances in the alpha pass, always write
- // out clip information.
- // TODO(gw): It's possible that we might want alpha
- // shaders that don't clip in the future,
- // but it's reasonable to assume that one
- // implies the other, for now.
+ // For brush instances in the alpha pass, always write
+ // out clip information.
+ // TODO(gw): It's possible that we might want alpha
+ // shaders that don't clip in the future,
+ // but it's reasonable to assume that one
+ // implies the other, for now.
#ifdef WR_FEATURE_ALPHA_PASS
- write_clip(
- vi.screen_pos,
- clip_area
- );
+ write_clip(
+ vi.screen_pos,
+ clip_area
+ );
#endif
- }
// Run the specific brush VS code to write interpolators.
brush_vs(
vi,
brush.prim_address + VECS_PER_BRUSH_PRIM,
brush_prim.local_rect,
brush.user_data,
pic_task
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -89,17 +89,21 @@ void brush_vs(
) / texture_size.xyxy;
break;
case RASTER_LOCAL:
default: {
f = (vi.local_pos - local_rect.p0) / local_rect.size;
// Set the clip bounds to a value that won't have any
// effect for local space images.
+#ifdef WR_FEATURE_TEXTURE_RECT
+ vUvClipBounds = vec4(0.0, 0.0, vec2(textureSize(sColor0)));
+#else
vUvClipBounds = vec4(0.0, 0.0, 1.0, 1.0);
+#endif
break;
}
}
#else
f = (vi.local_pos - local_rect.p0) / local_rect.size;
#endif
vUv.xy = mix(uv0, uv1, f);
deleted file mode 100644
--- a/gfx/webrender/res/cs_text_run.glsl
+++ /dev/null
@@ -1,68 +0,0 @@
-/* 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
-
-varying vec3 vUv;
-flat varying vec4 vColor;
-flat varying vec4 vStRect;
-
-#ifdef WR_VERTEX_SHADER
-// Draw a text run to a cache target. These are always
-// drawn un-transformed. These are used for effects such
-// as text-shadow.
-
-void main(void) {
- Primitive prim = load_primitive();
- TextRun text = fetch_text_run(prim.specific_prim_address);
-
- int glyph_index = prim.user_data0;
- int resource_address = prim.user_data1;
- int subpx_dir = prim.user_data2;
-
- Glyph glyph = fetch_glyph(prim.specific_prim_address,
- glyph_index,
- subpx_dir);
-
- GlyphResource res = fetch_glyph_resource(resource_address);
-
- // Scale from glyph space to local space.
- float scale = res.scale / uDevicePixelRatio;
-
- // Compute the glyph rect in local space.
- RectWithSize glyph_rect = RectWithSize(scale * res.offset + text.offset + glyph.offset,
- scale * (res.uv_rect.zw - res.uv_rect.xy));
-
- // Select the corner of the glyph rect that we are processing.
- vec2 local_pos = (glyph_rect.p0 + glyph_rect.size * aPosition.xy);
-
- // Clamp the local position to the text run's local clipping rectangle.
- local_pos = clamp_rect(local_pos, prim.local_clip_rect);
-
- // Move the point into device pixel space.
- local_pos = (local_pos - prim.task.content_origin) * uDevicePixelRatio;
- local_pos += prim.task.common_data.task_rect.p0;
- gl_Position = uTransform * vec4(local_pos, 0.0, 1.0);
-
- vec2 texture_size = vec2(textureSize(sColor0, 0));
- vec2 st0 = res.uv_rect.xy / texture_size;
- vec2 st1 = res.uv_rect.zw / texture_size;
-
- vUv = vec3(mix(st0, st1, aPosition.xy), res.layer);
- vColor = prim.task.color;
-
- // We clamp the texture coordinates to the half-pixel offset from the borders
- // in order to avoid sampling outside of the texture area.
- vec2 half_texel = vec2(0.5) / texture_size;
- vStRect = vec4(min(st0, st1) + half_texel, max(st0, st1) - half_texel);
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-void main(void) {
- vec2 uv = clamp(vUv.xy, vStRect.xy, vStRect.zw);
- float a = texture(sColor0, vec3(uv, vUv.z)).a;
- oFragColor = vColor * a;
-}
-#endif
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,27 +1,27 @@
/* 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/. */
use api::{AlphaType, DeviceIntRect, DeviceIntSize, LayerToWorldScale};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
-use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
+use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress};
use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, RasterizationSpace};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
-use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive};
+use picture::{PictureCompositeMode, PictureKind, PicturePrimitive};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{CachedGradient, ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PictureIndex, PrimitiveRun};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::BLOCKS_PER_UV_RECT;
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use std::{usize, f32, i32};
@@ -351,40 +351,31 @@ impl PrimitiveBatch {
instances: Vec::new(),
}
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AlphaBatchContainer {
- pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
pub opaque_batches: Vec<PrimitiveBatch>,
pub alpha_batches: Vec<PrimitiveBatch>,
pub target_rect: Option<DeviceIntRect>,
}
impl AlphaBatchContainer {
pub fn new(target_rect: Option<DeviceIntRect>) -> AlphaBatchContainer {
AlphaBatchContainer {
- text_run_cache_prims: FastHashMap::default(),
opaque_batches: Vec::new(),
alpha_batches: Vec::new(),
target_rect,
}
}
fn merge(&mut self, builder: AlphaBatchBuilder) {
- for (key, value) in builder.text_run_cache_prims {
- self.text_run_cache_prims
- .entry(key)
- .or_insert(vec![])
- .extend(value);
- }
-
for other_batch in builder.batch_list.opaque_batch_list.batches {
let batch_index = self.opaque_batches.iter().position(|batch| {
batch.key.is_compatible_with(&other_batch.key)
});
match batch_index {
Some(batch_index) => {
self.opaque_batches[batch_index].instances.extend(other_batch.instances);
@@ -415,30 +406,28 @@ impl AlphaBatchContainer {
}
}
}
}
/// Encapsulates the logic of building batches for items that are blended.
pub struct AlphaBatchBuilder {
pub batch_list: BatchList,
- pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
glyph_fetch_buffer: Vec<GlyphFetchResult>,
target_rect: DeviceIntRect,
}
impl AlphaBatchBuilder {
pub fn new(
screen_size: DeviceIntSize,
target_rect: DeviceIntRect,
) -> Self {
AlphaBatchBuilder {
batch_list: BatchList::new(screen_size),
glyph_fetch_buffer: Vec::new(),
- text_run_cache_prims: FastHashMap::default(),
target_rect,
}
}
pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer> {
self.batch_list.finalize();
let task_relative_target_rect = DeviceIntRect::new(
@@ -451,17 +440,16 @@ impl AlphaBatchBuilder {
if can_merge {
merged_batches.merge(self);
None
} else {
Some(AlphaBatchContainer {
alpha_batches: self.batch_list.alpha_batch_list.batches,
opaque_batches: self.batch_list.opaque_batch_list.batches,
target_rect: Some(self.target_rect),
- text_run_cache_prims: self.text_run_cache_prims,
})
}
}
pub fn add_pic_to_batch(
&mut self,
pic: &PicturePrimitive,
task_id: RenderTaskId,
@@ -495,17 +483,16 @@ impl AlphaBatchBuilder {
scroll_id,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
&mut splitter,
- pic,
content_origin,
);
}
// Flush the accumulated plane splits onto the task tree.
// Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
let prim_index = PrimitiveIndex(poly.anchor);
@@ -555,49 +542,35 @@ impl AlphaBatchBuilder {
scroll_id: ClipScrollNodeIndex,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
task_id: RenderTaskId,
task_address: RenderTaskAddress,
deferred_resolves: &mut Vec<DeferredResolve>,
splitter: &mut BspSplitter<f64, WorldPixel>,
- pic: &PicturePrimitive,
- content_origin: ContentOrigin,
+ content_origin: DeviceIntPoint,
) {
for i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
-
let metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
- // Now that we walk the primitive runs in order to add
- // items to batches, we need to check if they are
- // visible here.
- // We currently only support culling on normal (Image)
- // picture types.
- // TODO(gw): Support culling on shadow image types.
- let is_image = match pic.kind {
- PictureKind::Image { .. } => true,
- PictureKind::TextShadow { .. } => false,
- };
-
- if !is_image || metadata.screen_rect.is_some() {
+ if metadata.screen_rect.is_some() {
self.add_prim_to_batch(
metadata.clip_chain_rect_index,
scroll_id,
prim_index,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
splitter,
content_origin,
- pic,
);
}
}
}
// Adds a primitive to a batch.
// It can recursively call itself in some situations, for
// example if it encounters a picture where the items
@@ -609,51 +582,34 @@ impl AlphaBatchBuilder {
prim_index: PrimitiveIndex,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
task_id: RenderTaskId,
task_address: RenderTaskAddress,
deferred_resolves: &mut Vec<DeferredResolve>,
splitter: &mut BspSplitter<f64, WorldPixel>,
- content_origin: ContentOrigin,
- pic: &PicturePrimitive,
+ content_origin: DeviceIntPoint,
) {
let z = prim_index.0 as i32;
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
let scroll_node = &ctx.node_data[scroll_id.0 as usize];
// TODO(gw): Calculating this for every primitive is a bit
// wasteful. We should probably cache this in
// the scroll node...
let transform_kind = scroll_node.transform.transform_kind();
- let task_relative_bounding_rect = match content_origin {
- ContentOrigin::Screen(point) => {
- // translate by content-origin
- let screen_rect = prim_metadata.screen_rect.expect("bug");
- DeviceIntRect::new(
- DeviceIntPoint::new(
- screen_rect.unclipped.origin.x - point.x,
- screen_rect.unclipped.origin.y - point.y,
- ),
- screen_rect.unclipped.size,
- )
- }
- ContentOrigin::Local(point) => {
- // scale local rect by device pixel ratio
- let content_rect = LayerRect::new(
- LayerPoint::new(
- prim_metadata.local_rect.origin.x - point.x,
- prim_metadata.local_rect.origin.y - point.y,
- ),
- prim_metadata.local_rect.size,
- );
- (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round().to_i32()
- }
- };
+ let screen_rect = prim_metadata.screen_rect.expect("bug");
+ let task_relative_bounding_rect = DeviceIntRect::new(
+ DeviceIntPoint::new(
+ screen_rect.unclipped.origin.x - content_origin.x,
+ screen_rect.unclipped.origin.y - content_origin.y,
+ ),
+ screen_rect.unclipped.size,
+ );
let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location);
let no_textures = BatchTextures::no_texture();
let clip_task_address = prim_metadata
.clip_task_id
.map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
let base_instance = SimplePrimitiveInstance::new(
prim_cache_address,
@@ -704,21 +660,21 @@ impl AlphaBatchBuilder {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
- brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
+ brush_flags: BrushFlags::empty(),
user_data: [
uv_rect_address,
BrushImageSourceKind::Color as i32,
- RasterizationSpace::Local as i32,
+ RasterizationSpace::Screen as i32,
],
};
batch.push(PrimitiveInstance::from(instance));
}
PictureKind::Image {
composite_mode,
secondary_render_task_id,
is_in_3d_context,
@@ -1131,37 +1087,24 @@ impl AlphaBatchBuilder {
},
);
let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0));
}
PrimitiveKind::TextRun => {
let text_cpu =
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
- let is_shadow = match pic.kind {
- PictureKind::TextShadow { .. } => true,
- PictureKind::Image { .. } => false,
- };
-
- // TODO(gw): It probably makes sense to base this decision on the content
- // origin field in the future (once that's configurable).
- let font_transform = if is_shadow {
- None
- } else {
- Some(scroll_node.transform)
- };
let font = text_cpu.get_font(
ctx.device_pixel_scale,
- font_transform,
+ Some(scroll_node.transform),
);
let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
let batch_list = &mut self.batch_list;
- let text_run_cache_prims = &mut self.text_run_cache_prims;
ctx.resource_cache.fetch_glyphs(
font,
&text_cpu.glyph_keys,
glyph_fetch_buffer,
gpu_cache,
|texture_id, mut glyph_format, glyphs| {
debug_assert_ne!(texture_id, SourceTexture::Invalid);
@@ -1172,54 +1115,48 @@ impl AlphaBatchBuilder {
}
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 batch = if is_shadow {
- text_run_cache_prims
- .entry(texture_id)
- .or_insert(Vec::new())
- } else {
- let textures = BatchTextures {
- colors: [
- texture_id,
- SourceTexture::Invalid,
- SourceTexture::Invalid,
- ],
- };
+ let textures = BatchTextures {
+ colors: [
+ texture_id,
+ SourceTexture::Invalid,
+ SourceTexture::Invalid,
+ ],
+ };
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::TextRun(glyph_format),
- );
+ let kind = BatchKind::Transformable(
+ transform_kind,
+ TransformBatchKind::TextRun(glyph_format),
+ );
- let blend_mode = match glyph_format {
- GlyphFormat::Subpixel |
- GlyphFormat::TransformedSubpixel => {
- if text_cpu.font.bg_color.a != 0 {
- BlendMode::SubpixelWithBgColor
- } else if ctx.use_dual_source_blending {
- BlendMode::SubpixelDualSource
- } else {
- BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
- }
+ let blend_mode = match glyph_format {
+ GlyphFormat::Subpixel |
+ GlyphFormat::TransformedSubpixel => {
+ if text_cpu.font.bg_color.a != 0 {
+ BlendMode::SubpixelWithBgColor
+ } else if ctx.use_dual_source_blending {
+ BlendMode::SubpixelDualSource
+ } else {
+ BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
}
- GlyphFormat::Alpha |
- GlyphFormat::TransformedAlpha |
- GlyphFormat::Bitmap |
- GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
- };
+ }
+ GlyphFormat::Alpha |
+ GlyphFormat::TransformedAlpha |
+ GlyphFormat::Bitmap |
+ GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
+ };
- let key = BatchKey::new(kind, blend_mode, textures);
- batch_list.get_suitable_batch(key, &task_relative_bounding_rect)
- };
+ let key = BatchKey::new(kind, blend_mode, textures);
+ let batch = batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
for glyph in glyphs {
batch.push(base_instance.build(
glyph.index_in_text_run,
glyph.uv_rect_address.as_int(),
subpx_dir as u32 as i32,
));
}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -10,29 +10,30 @@ use api::{FilterOp, FontInstanceKey, Fon
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayerPoint, LayerPrimitiveInfo};
use api::{LayerRect, LayerSize, LayerVector2D, LayoutRect, LayoutSize, LayoutTransform};
use api::{LayoutVector2D, LineOrientation, LineStyle, LocalClip, PipelineId, PropertyBinding};
use api::{RepeatMode, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity, Shadow};
use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect, TileOffset};
use api::{TransformStyle, YuvColorSpace, YuvData};
use app_units::Au;
use border::ImageBorderSegment;
+use box_shadow::{BLUR_SAMPLE_SCALE};
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use hit_test::{HitTestingItem, HitTestingRun};
use image::{decompose_image, TiledImageInfo};
use internal_types::{FastHashMap, FastHashSet};
use picture::{PictureCompositeMode, PictureKind};
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
use prim_store::{CachedGradientIndex, ImageCacheKey, ImagePrimitiveCpu, ImageSource};
-use prim_store::{PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
+use prim_store::{PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest, TiledImageMap};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::{BuiltScene, SceneRequest};
use std::{f32, mem, usize};
use tiling::{CompositeOps, ScrollbarPrimitive};
use util::{MaxRect, RectHelpers, recycle_vec};
@@ -734,18 +735,18 @@ impl<'a> DisplayListFlattener<'a> {
info.tile_spacing,
);
}
SpecificDisplayItem::RadialGradient(ref info) => {
self.add_radial_gradient(
clip_and_scroll,
&prim_info,
info.gradient.center,
- info.gradient.start_radius,
- info.gradient.end_radius,
+ info.gradient.start_offset * info.gradient.radius.width,
+ info.gradient.end_offset * info.gradient.radius.width,
info.gradient.radius.width / info.gradient.radius.height,
item.gradient_stops(),
info.gradient.extend_mode,
info.tile_size,
info.tile_spacing,
);
}
SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
@@ -1514,23 +1515,29 @@ impl<'a> DisplayListFlattener<'a> {
color: line_color.premultiplied(),
style,
orientation,
},
None,
);
let mut fast_shadow_prims = Vec::new();
+ let mut slow_shadow_prims = Vec::new();
for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
let brush = &self.prim_store.cpu_brushes[shadow_metadata.cpu_prim_index.0];
- let picture = &self.prim_store.pictures[brush.get_picture_index().0];
+ let pic_index = brush.get_picture_index();
+ let picture = &self.prim_store.pictures[pic_index.0];
match picture.kind {
- PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
- fast_shadow_prims.push((idx, offset, color));
+ PictureKind::TextShadow { offset, color, blur_radius, .. } => {
+ if blur_radius == 0.0 {
+ fast_shadow_prims.push((idx, offset, color));
+ } else {
+ slow_shadow_prims.push((pic_index, offset, color));
+ }
}
_ => {}
}
}
for (idx, shadow_offset, shadow_color) in fast_shadow_prims {
let line = BrushPrimitive::new(
BrushKind::Line {
@@ -1547,47 +1554,56 @@ impl<'a> DisplayListFlattener<'a> {
let prim_index = self.create_primitive(
&info,
Vec::new(),
PrimitiveContainer::Brush(line),
);
self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
}
- let prim_index = self.create_primitive(
- &info,
- Vec::new(),
- PrimitiveContainer::Brush(line),
- );
+ if line_color.a > 0.0 {
+ let prim_index = self.create_primitive(
+ &info,
+ Vec::new(),
+ PrimitiveContainer::Brush(line),
+ );
- if line_color.a > 0.0 {
if self.shadow_prim_stack.is_empty() {
self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
} else {
self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
}
}
- for &(shadow_prim_index, _) in &self.shadow_prim_stack {
- let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
- debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Brush);
- let brush = &self.prim_store.cpu_brushes[shadow_metadata.cpu_prim_index.0];
- let picture = &mut self.prim_store.pictures[brush.get_picture_index().0];
+ for (pic_index, shadow_offset, shadow_color) in slow_shadow_prims {
+ let line = BrushPrimitive::new(
+ BrushKind::Line {
+ wavy_line_thickness,
+ color: shadow_color.premultiplied(),
+ style,
+ orientation,
+ },
+ None,
+ );
+ let mut info = info.clone();
+ info.rect = info.rect.translate(&shadow_offset);
+ info.clip_rect = info.clip_rect.translate(&shadow_offset);
+ let prim_index = self.create_primitive(
+ &info,
+ Vec::new(),
+ PrimitiveContainer::Brush(line),
+ );
- match picture.kind {
- // Only run real blurs here (fast path zero blurs are handled above).
- PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
- picture.add_primitive(
- prim_index,
- clip_and_scroll,
- );
- }
- _ => {}
- }
+ let picture = &mut self.prim_store.pictures[pic_index.0];
+
+ picture.add_primitive(
+ prim_index,
+ clip_and_scroll,
+ );
}
}
pub fn add_border(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
border_item: &BorderDisplayItem,
@@ -1819,18 +1835,18 @@ impl<'a> DisplayListFlattener<'a> {
let segment_rel = segment.origin - rect.origin;
let mut info = info.clone();
info.rect = segment;
self.add_radial_gradient(
clip_and_scroll,
&info,
border.gradient.center - segment_rel,
- border.gradient.start_radius,
- border.gradient.end_radius,
+ border.gradient.start_offset * border.gradient.radius.width,
+ border.gradient.end_offset * border.gradient.radius.width,
border.gradient.radius.width / border.gradient.radius.height,
gradient_stops,
border.gradient.extend_mode,
segment.size,
LayerSize::zero(),
);
}
}
@@ -2015,17 +2031,17 @@ impl<'a> DisplayListFlattener<'a> {
}
}
}
pub fn add_text(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
run_offset: LayoutVector2D,
- info: &LayerPrimitiveInfo,
+ prim_info: &LayerPrimitiveInfo,
font_instance_key: &FontInstanceKey,
text_color: &ColorF,
glyph_range: ItemRange<GlyphInstance>,
glyph_count: usize,
glyph_options: Option<GlyphOptions>,
) {
let prim = {
let instance_map = self.font_instances.read().unwrap();
@@ -2106,86 +2122,102 @@ impl<'a> DisplayListFlattener<'a> {
// text elements to get pixel perfect results for reftests. It's also a big
// performance win to avoid blurs and render target allocations where
// possible. For any text shadows that have zero blur, create a normal text
// primitive with the shadow's color and offset. These need to be added
// *before* the visual text primitive in order to get the correct paint
// order. Store them in a Vec first to work around borrowck issues.
// TODO(gw): Refactor to avoid having to store them in a Vec first.
let mut fast_shadow_prims = Vec::new();
+ let mut slow_shadow_prims = Vec::new();
for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
let brush = &self.prim_store.cpu_brushes[shadow_metadata.cpu_prim_index.0];
- let picture_prim = &self.prim_store.pictures[brush.get_picture_index().0];
+ let pic_index = brush.get_picture_index();
+ let picture_prim = &self.prim_store.pictures[pic_index.0];
match picture_prim.kind {
- PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
+ PictureKind::TextShadow { offset, color, blur_radius, .. } => {
let mut text_prim = prim.clone();
text_prim.font.color = color.into();
text_prim.shadow = true;
text_prim.offset += offset;
- fast_shadow_prims.push((idx, text_prim));
+
+ if blur_radius == 0.0 {
+ fast_shadow_prims.push((idx, text_prim, offset));
+ } else {
+ text_prim.font.render_mode = text_prim
+ .font
+ .render_mode
+ .limit_by(FontRenderMode::Alpha);
+
+ slow_shadow_prims.push((pic_index, text_prim, offset, blur_radius));
+ }
}
_ => {}
}
}
- for (idx, text_prim) in fast_shadow_prims {
- let rect = info.rect;
- let mut info = info.clone();
- info.rect = rect.translate(&text_prim.offset);
- info.clip_rect = info.clip_rect.translate(&text_prim.offset);
+ for (idx, text_prim, offset) in fast_shadow_prims {
+ let rect = prim_info.rect;
+ let mut info = prim_info.clone();
+ info.rect = rect.translate(&offset);
+ info.clip_rect = info.clip_rect.translate(&offset);
let prim_index = self.create_primitive(
&info,
Vec::new(),
PrimitiveContainer::TextRun(text_prim),
);
self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
}
- // Create (and add to primitive store) the primitive that will be
- // used for both the visual element and also the shadow(s).
- let prim_index = self.create_primitive(
- info,
- Vec::new(),
- PrimitiveContainer::TextRun(prim),
- );
-
// Only add a visual element if it can contribute to the scene.
if text_color.a > 0.0 {
+ // Create (and add to primitive store) the primitive that will be
+ // used for both the visual element and also the shadow(s).
+ let prim_index = self.create_primitive(
+ prim_info,
+ Vec::new(),
+ PrimitiveContainer::TextRun(prim),
+ );
+
if self.shadow_prim_stack.is_empty() {
- self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
+ self.add_primitive_to_hit_testing_list(prim_info, clip_and_scroll);
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
} else {
- self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
+ self.pending_shadow_contents.push((prim_index, clip_and_scroll, *prim_info));
}
}
// Now add this primitive index to all the currently active text shadow
// primitives. Although we're adding the indices *after* the visual
// primitive here, they will still draw before the visual text, since
// the shadow primitive itself has been added to the draw cmd
// list *before* the visual element, during push_shadow. We need
// the primitive index of the visual element here before we can add
// the indices as sub-primitives to the shadow primitives.
- for &(shadow_prim_index, _) in &self.shadow_prim_stack {
- let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
- debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Brush);
- let brush = &self.prim_store.cpu_brushes[shadow_metadata.cpu_prim_index.0];
- let picture = &mut self.prim_store.pictures[brush.get_picture_index().0];
+ for (pic_index, shadow_prim, offset, blur_radius) in slow_shadow_prims {
+ let blur_region = blur_radius * BLUR_SAMPLE_SCALE;
+
+ let rect = prim_info.rect;
+ let mut info = prim_info.clone();
+ info.rect = rect.translate(&offset).inflate(blur_region, blur_region);
+ info.clip_rect = info.clip_rect.translate(&offset);
- match picture.kind {
- // Only run real blurs here (fast path zero blurs are handled above).
- PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
- picture.add_primitive(
- prim_index,
- clip_and_scroll,
- );
- }
- _ => {}
- }
+ let prim_index = self.create_primitive(
+ &info,
+ Vec::new(),
+ PrimitiveContainer::TextRun(shadow_prim),
+ );
+
+ let picture = &mut self.prim_store.pictures[pic_index.0];
+
+ picture.add_primitive(
+ prim_index,
+ clip_and_scroll,
+ );
}
}
pub fn add_image(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
stretch_size: LayerSize,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -8,17 +8,16 @@ use api::{LayerRect, LayerSize, Pipeline
use clip::{ClipChain, ClipStore};
use clip_scroll_node::{ClipScrollNode};
use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
use display_list_flattener::{DisplayListFlattener};
use gpu_cache::GpuCache;
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, PictureType};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
-use picture::{ContentOrigin};
use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveRun, PrimitiveStore};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, f32};
use std::sync::Arc;
@@ -27,17 +26,16 @@ use tiling::ScrollbarPrimitive;
use util::{self, MaxRect, WorldToLayerFastTransform};
#[derive(Clone, Copy)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FrameBuilderConfig {
pub enable_scrollbars: bool,
pub default_font_render_mode: FontRenderMode,
- pub debug: bool,
pub dual_source_blending_is_supported: bool,
pub dual_source_blending_is_enabled: bool,
}
/// A builder structure for `tiling::Frame`
pub struct FrameBuilder {
screen_rect: DeviceUintRect,
background_color: Option<ColorF>,
@@ -66,22 +64,21 @@ pub struct FrameBuildingState<'a> {
pub local_clip_rects: &'a mut Vec<LayerRect>,
pub resource_cache: &'a mut ResourceCache,
pub gpu_cache: &'a mut GpuCache,
pub cached_gradients: &'a mut [CachedGradient],
}
pub struct PictureContext<'a> {
pub pipeline_id: PipelineId,
- pub perform_culling: bool,
pub prim_runs: Vec<PrimitiveRun>,
pub original_reference_frame_index: Option<ClipScrollNodeIndex>,
pub display_list: &'a BuiltDisplayList,
- pub draw_text_transformed: bool,
pub inv_world_transform: Option<WorldToLayerFastTransform>,
+ pub apply_local_clip_rect: bool,
}
pub struct PictureState {
pub tasks: Vec<RenderTaskId>,
}
impl PictureState {
pub fn new() -> PictureState {
@@ -120,17 +117,16 @@ impl FrameBuilder {
prim_store: PrimitiveStore::new(),
clip_store: ClipStore::new(),
screen_rect: DeviceUintRect::zero(),
window_size: DeviceUintSize::zero(),
background_color: None,
config: FrameBuilderConfig {
enable_scrollbars: false,
default_font_render_mode: FontRenderMode::Mono,
- debug: false,
dual_source_blending_is_enabled: true,
dual_source_blending_is_supported: false,
},
}
}
pub fn with_display_list_flattener(
screen_rect: DeviceUintRect,
@@ -197,22 +193,21 @@ impl FrameBuilder {
local_clip_rects,
resource_cache,
gpu_cache,
cached_gradients: &mut self.cached_gradients,
};
let pic_context = PictureContext {
pipeline_id: root_clip_scroll_node.pipeline_id,
- perform_culling: true,
prim_runs: mem::replace(&mut self.prim_store.pictures[0].runs, Vec::new()),
original_reference_frame_index: None,
display_list,
- draw_text_transformed: true,
inv_world_transform: None,
+ apply_local_clip_rect: true,
};
let mut pic_state = PictureState::new();
self.prim_store.reset_prim_visibility();
self.prim_store.prepare_prim_runs(
&pic_context,
&mut pic_state,
@@ -222,17 +217,17 @@ impl FrameBuilder {
let pic = &mut self.prim_store.pictures[0];
pic.runs = pic_context.prim_runs;
let root_render_task = RenderTask::new_picture(
RenderTaskLocation::Fixed(frame_context.screen_rect),
PrimitiveIndex(0),
RenderTargetKind::Color,
- ContentOrigin::Screen(DeviceIntPoint::zero()),
+ DeviceIntPoint::zero(),
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state.tasks,
PictureType::Image,
);
let render_task_id = frame_state.render_tasks.add(root_render_task);
pic.surface = Some(render_task_id);
--- a/gfx/webrender/src/freelist.rs
+++ b/gfx/webrender/src/freelist.rs
@@ -60,35 +60,38 @@ struct Slot<T> {
value: Option<T>,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FreeList<T> {
slots: Vec<Slot<T>>,
free_list_head: Option<u32>,
+ active_count: usize,
}
pub enum UpsertResult<T> {
Updated(T),
Inserted(FreeListHandle<T>),
}
impl<T> FreeList<T> {
pub fn new() -> Self {
FreeList {
slots: Vec::new(),
free_list_head: None,
+ active_count: 0,
}
}
pub fn recycle(self) -> FreeList<T> {
FreeList {
slots: recycle_vec(self.slots),
free_list_head: None,
+ active_count: 0,
}
}
#[allow(dead_code)]
pub fn get(&self, id: &FreeListHandle<T>) -> &T {
self.slots[id.index as usize].value.as_ref().unwrap()
}
@@ -126,16 +129,18 @@ impl<T> FreeList<T> {
slot.value = Some(data);
result
} else {
UpsertResult::Inserted(self.insert(data))
}
}
pub fn insert(&mut self, item: T) -> FreeListHandle<T> {
+ self.active_count += 1;
+
match self.free_list_head {
Some(free_index) => {
let slot = &mut self.slots[free_index as usize];
// Remove from free list.
self.free_list_head = slot.next;
slot.next = None;
slot.value = Some(item);
@@ -161,15 +166,20 @@ impl<T> FreeList<T> {
epoch,
_marker: PhantomData,
}
}
}
}
pub fn free(&mut self, id: FreeListHandle<T>) -> T {
+ self.active_count -= 1;
let slot = &mut self.slots[id.index as usize];
slot.next = self.free_list_head;
slot.epoch = Epoch(slot.epoch.0 + 1);
self.free_list_head = Some(id.index);
slot.value.take().unwrap()
}
+
+ pub fn len(&self) -> usize {
+ self.active_count
+ }
}
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -516,16 +516,17 @@ impl GlyphRasterizer {
texture_cache.update(
&mut texture_cache_handle,
ImageDescriptor {
width: glyph.width,
height: glyph.height,
stride: None,
format: ImageFormat::BGRA8,
is_opaque: false,
+ allow_mipmaps: false,
offset: 0,
},
TextureFilter::Linear,
Some(ImageData::Raw(Arc::new(glyph.bytes))),
[glyph.left, -glyph.top, glyph.scale],
None,
gpu_cache,
Some(glyph_key_cache.eviction_notice()),
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,25 +1,24 @@
/* 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/. */
-use api::{DeviceIntPoint, DeviceIntRect};
-use api::{LayerPoint, LayerRect, LayerToWorldScale, LayerVector2D};
use api::{ColorF, FilterOp, MixBlendMode, PipelineId};
+use api::{DeviceIntRect, LayerRect, LayerToWorldScale, LayerVector2D};
use api::{PremultipliedColorF, Shadow};
use box_shadow::{BLUR_SAMPLE_SCALE};
use clip_scroll_tree::ClipScrollNodeIndex;
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState};
use gpu_cache::{GpuCacheHandle, GpuDataRequest};
use gpu_types::{PictureType};
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use prim_store::{PrimitiveMetadata, ScrollNodeAndClipChain};
use render_task::{ClearMode, RenderTask};
-use render_task::{RenderTaskId, RenderTaskLocation, to_cache_size};
+use render_task::{RenderTaskId, RenderTaskLocation};
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
/*
A picture represents a dynamically rendered image. It consists of:
* A number of primitives that are drawn onto the picture.
* A composite operation describing how to composite this
@@ -36,33 +35,22 @@ pub enum PictureCompositeMode {
MixBlend(MixBlendMode),
/// Apply a CSS filter.
Filter(FilterOp),
/// Draw to intermediate surface, copy straight across. This
/// is used for CSS isolation, and plane splitting.
Blit,
}
-/// Configure whether the content to be drawn by a picture
-/// in local space rasterization or the screen space.
-#[derive(Debug, Copy, Clone, PartialEq)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub enum ContentOrigin {
- Local(LayerPoint),
- Screen(DeviceIntPoint),
-}
-
#[derive(Debug)]
pub enum PictureKind {
TextShadow {
offset: LayerVector2D,
color: ColorF,
blur_radius: f32,
- content_rect: LayerRect,
},
Image {
// If a mix-blend-mode, contains the render task for
// the readback of the framebuffer that we use to sample
// from in the mix-blend-mode shader.
// For drop-shadow filter, this will store the original
// picture task which would be rendered on screen after
// blur pass.
@@ -80,19 +68,16 @@ pub enum PictureKind {
// It is only different if this is part of a 3D
// rendering context.
reference_frame_index: ClipScrollNodeIndex,
real_local_rect: LayerRect,
// An optional cache handle for storing extra data
// in the GPU cache, depending on the type of
// picture.
extra_gpu_data_handle: GpuCacheHandle,
- // The current screen-space rect of the rendered
- // portion of this picture.
- task_rect: DeviceIntRect,
},
}
#[derive(Debug)]
pub struct PicturePrimitive {
// If this picture is drawn to an intermediate surface,
// the associated target information.
pub surface: Option<RenderTaskId>,
@@ -101,35 +86,33 @@ pub struct PicturePrimitive {
pub kind: PictureKind,
// List of primitive runs that make up this picture.
pub runs: Vec<PrimitiveRun>,
// The pipeline that the primitives on this picture belong to.
pub pipeline_id: PipelineId,
- // If true, apply visibility culling to primitives on this
- // picture. For text shadows and box shadows, we want to
- // unconditionally draw them.
- pub cull_children: bool,
+ // The current screen-space rect of the rendered
+ // portion of this picture.
+ task_rect: DeviceIntRect,
}
impl PicturePrimitive {
pub fn new_text_shadow(shadow: Shadow, pipeline_id: PipelineId) -> Self {
PicturePrimitive {
runs: Vec::new(),
surface: None,
kind: PictureKind::TextShadow {
offset: shadow.offset,
color: shadow.color,
blur_radius: shadow.blur_radius,
- content_rect: LayerRect::zero(),
},
pipeline_id,
- cull_children: false,
+ task_rect: DeviceIntRect::zero(),
}
}
pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
match self.kind {
PictureKind::Image { ref mut composite_mode, .. } => {
match composite_mode {
&mut Some(PictureCompositeMode::Filter(ref mut filter)) => {
@@ -162,20 +145,19 @@ impl PicturePrimitive {
kind: PictureKind::Image {
secondary_render_task_id: None,
composite_mode,
is_in_3d_context,
frame_output_pipeline_id,
reference_frame_index,
real_local_rect: LayerRect::zero(),
extra_gpu_data_handle: GpuCacheHandle::new(),
- task_rect: DeviceIntRect::zero(),
},
pipeline_id,
- cull_children: true,
+ task_rect: DeviceIntRect::zero(),
}
}
pub fn add_primitive(
&mut self,
prim_index: PrimitiveIndex,
clip_and_scroll: ScrollNodeAndClipChain
) {
@@ -214,25 +196,23 @@ impl PicturePrimitive {
local_content_rect.inflate(inflate_size, inflate_size)
.translate(&offset)
}
_ => {
local_content_rect
}
}
}
- PictureKind::TextShadow { offset, blur_radius, ref mut content_rect, .. } => {
+ PictureKind::TextShadow { blur_radius, .. } => {
let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
- *content_rect = local_content_rect.inflate(
+ local_content_rect.inflate(
blur_offset,
blur_offset,
- );
-
- content_rect.translate(&offset)
+ )
}
}
}
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_metadata: &mut PrimitiveMetadata,
@@ -241,26 +221,26 @@ impl PicturePrimitive {
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) {
let content_scale = LayerToWorldScale::new(1.0) * frame_context.device_pixel_scale;
let prim_screen_rect = prim_metadata
.screen_rect
.as_ref()
.expect("bug: trying to draw an off-screen picture!?");
+ let device_rect;
match self.kind {
PictureKind::Image {
ref mut secondary_render_task_id,
ref mut extra_gpu_data_handle,
- ref mut task_rect,
composite_mode,
..
} => {
- let device_rect = match composite_mode {
+ device_rect = match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
// If blur radius is 0, we can skip drawing this an an
// intermediate surface.
if blur_radius == 0.0 {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
DeviceIntRect::zero()
@@ -277,23 +257,21 @@ impl PicturePrimitive {
// then intersect with the total screen rect, to minimize the
// allocation size.
let device_rect = prim_screen_rect
.clipped
.inflate(blur_range, blur_range)
.intersection(&prim_screen_rect.unclipped)
.unwrap();
- let content_origin = ContentOrigin::Screen(device_rect.origin);
-
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, device_rect.size),
prim_index,
RenderTargetKind::Color,
- content_origin,
+ device_rect.origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
let picture_task_id = frame_state.render_tasks.add(picture_task);
@@ -315,17 +293,17 @@ impl PicturePrimitive {
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => {
// TODO(gw): This is totally wrong and can never work with
// transformed drop-shadow elements. Fix me!
let rect = (prim_metadata.local_rect.translate(&-offset) * content_scale).round().to_i32();
let mut picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, rect.size),
prim_index,
RenderTargetKind::Color,
- ContentOrigin::Screen(rect.origin),
+ rect.origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
picture_task.mark_for_saving();
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
@@ -343,23 +321,21 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
rect
}
Some(PictureCompositeMode::MixBlend(..)) => {
- let content_origin = ContentOrigin::Screen(prim_screen_rect.clipped.origin);
-
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
RenderTargetKind::Color,
- content_origin,
+ prim_screen_rect.clipped.origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
let readback_task_id = frame_state.render_tasks.add(
RenderTask::new_readback(prim_screen_rect.clipped)
@@ -370,18 +346,16 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
prim_screen_rect.clipped
}
Some(PictureCompositeMode::Filter(filter)) => {
- let content_origin = ContentOrigin::Screen(prim_screen_rect.clipped.origin);
-
// If this filter is not currently going to affect
// the picture, just collapse this picture into the
// current render task. This most commonly occurs
// when opacity == 1.0, but can also occur on other
// filters and be a significant performance win.
if filter.is_noop() {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
@@ -394,38 +368,36 @@ impl PicturePrimitive {
}
}
}
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
RenderTargetKind::Color,
- content_origin,
+ prim_screen_rect.clipped.origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
}
prim_screen_rect.clipped
}
Some(PictureCompositeMode::Blit) => {
- let content_origin = ContentOrigin::Screen(prim_screen_rect.clipped.origin);
-
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
RenderTargetKind::Color,
- content_origin,
+ prim_screen_rect.clipped.origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
@@ -435,44 +407,42 @@ impl PicturePrimitive {
}
None => {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
DeviceIntRect::zero()
}
};
-
- // If scrolling or property animation has resulted in the task
- // rect being different than last time, invalidate the GPU
- // cache entry for this picture to ensure that the correct
- // task rect is provided to the image shader.
- if *task_rect != device_rect {
- frame_state.gpu_cache.invalidate(&prim_metadata.gpu_location);
- *task_rect = device_rect;
- }
}
- PictureKind::TextShadow { blur_radius, color, content_rect, .. } => {
+ PictureKind::TextShadow { blur_radius, color, .. } => {
// This is a shadow element. Create a render task that will
// render the text run to a target, and then apply a gaussian
// blur to that text run in order to build the actual primitive
// which will be blitted to the framebuffer.
- let cache_size = to_cache_size(content_rect.size * content_scale);
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
let device_radius = (blur_radius * frame_context.device_pixel_scale.0).round();
let blur_std_deviation = device_radius * 0.5;
+ let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+
+ device_rect = prim_screen_rect
+ .clipped
+ .inflate(blur_range, blur_range)
+ .intersection(&prim_screen_rect.unclipped)
+ .unwrap();
+
let picture_task = RenderTask::new_picture(
- RenderTaskLocation::Dynamic(None, cache_size),
+ RenderTaskLocation::Dynamic(None, device_rect.size),
prim_index,
RenderTargetKind::Color,
- ContentOrigin::Local(content_rect.origin),
+ device_rect.origin,
color.premultiplied(),
ClearMode::Transparent,
Vec::new(),
PictureType::TextShadow,
);
let picture_task_id = frame_state.render_tasks.add(picture_task);
@@ -484,32 +454,41 @@ impl PicturePrimitive {
ClearMode::Transparent,
);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
}
}
+
+ // If scrolling or property animation has resulted in the task
+ // rect being different than last time, invalidate the GPU
+ // cache entry for this picture to ensure that the correct
+ // task rect is provided to the image shader.
+ if self.task_rect != device_rect {
+ frame_state.gpu_cache.invalidate(&prim_metadata.gpu_location);
+ self.task_rect = device_rect;
+ }
}
pub fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
+ request.push(self.task_rect.to_f32());
+
match self.kind {
PictureKind::TextShadow { .. } => {
- request.push([0.0; 4]);
request.push(PremultipliedColorF::WHITE);
}
- PictureKind::Image { task_rect, composite_mode, .. } => {
+ PictureKind::Image { composite_mode, .. } => {
let color = match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, _, color))) => {
color.premultiplied()
}
_ => {
PremultipliedColorF::WHITE
}
};
- request.push(task_rect.to_f32());
request.push(color);
}
}
}
}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1102,21 +1102,17 @@ impl PrimitiveStore {
frame_state: &mut FrameBuildingState,
) {
let metadata = &mut self.cpu_metadata[prim_index.0];
match metadata.prim_kind {
PrimitiveKind::Border => {}
PrimitiveKind::TextRun => {
let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
// The transform only makes sense for screen space rasterization
- let transform = if pic_context.draw_text_transformed {
- Some(prim_run_context.scroll_node.world_content_transform.into())
- } else {
- None
- };
+ let transform = Some(prim_run_context.scroll_node.world_content_transform.into());
text.prepare_for_render(
frame_state.resource_cache,
frame_context.device_pixel_scale,
transform,
pic_context.display_list,
frame_state.gpu_cache,
);
}
@@ -1710,18 +1706,17 @@ impl PrimitiveStore {
let mut may_need_clip_mask = true;
let mut pic_state_for_children = PictureState::new();
// Do some basic checks first, that can early out
// without even knowing the local rect.
let (prim_kind, cpu_prim_index) = {
let metadata = &self.cpu_metadata[prim_index.0];
- if pic_context.perform_culling &&
- !metadata.is_backface_visible &&
+ if !metadata.is_backface_visible &&
prim_run_context.scroll_node.world_content_transform.is_backface_visible() {
return None;
}
(metadata.prim_kind, metadata.cpu_prim_index)
};
// If we have dependencies, we need to prepare them first, in order
@@ -1733,17 +1728,17 @@ impl PrimitiveStore {
if let BrushKind::Picture { pic_index } = self.cpu_brushes[cpu_prim_index.0].kind {
let pic_context_for_children = {
let pic = &mut self.pictures[pic_index.0];
if !pic.resolve_scene_properties(frame_context.scene_properties) {
return None;
}
- let (draw_text_transformed, original_reference_frame_index) = match pic.kind {
+ let (apply_local_clip_rect, original_reference_frame_index) = match pic.kind {
PictureKind::Image { reference_frame_index, composite_mode, .. } => {
may_need_clip_mask = composite_mode.is_some();
(true, Some(reference_frame_index))
}
PictureKind::TextShadow { .. } => {
(false, None)
}
};
@@ -1756,22 +1751,21 @@ impl PrimitiveStore {
let inv_world_transform = prim_run_context
.scroll_node
.world_content_transform
.inverse();
PictureContext {
pipeline_id: pic.pipeline_id,
- perform_culling: pic.cull_children,
prim_runs: mem::replace(&mut pic.runs, Vec::new()),
original_reference_frame_index,
display_list,
- draw_text_transformed,
inv_world_transform,
+ apply_local_clip_rect,
}
};
let result = self.prepare_prim_runs(
&pic_context_for_children,
&mut pic_state_for_children,
frame_context,
frame_state,
@@ -1792,18 +1786,17 @@ impl PrimitiveStore {
metadata.local_rect.size.height <= 0.0 {
//warn!("invalid primitive rect {:?}", metadata.local_rect);
return None;
}
let local_rect = metadata.local_clip_rect.intersection(&metadata.local_rect);
let local_rect = match local_rect {
Some(local_rect) => local_rect,
- None if pic_context.perform_culling => return None,
- None => LayerRect::zero(),
+ None => return None,
};
let screen_bounding_rect = calculate_screen_bounding_rect(
&prim_run_context.scroll_node.world_content_transform,
&local_rect,
frame_context.device_pixel_scale,
);
@@ -1811,26 +1804,26 @@ impl PrimitiveStore {
.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)
.map(|clipped| {
ScreenRect {
clipped,
unclipped: screen_bounding_rect,
}
});
- if metadata.screen_rect.is_none() && pic_context.perform_culling {
+ if metadata.screen_rect.is_none() {
return None;
}
metadata.clip_chain_rect_index = prim_run_context.clip_chain_rect_index;
(local_rect, screen_bounding_rect)
};
- if pic_context.perform_culling && may_need_clip_mask && !self.update_clip_task(
+ if may_need_clip_mask && !self.update_clip_task(
prim_index,
prim_run_context,
&unclipped_device_rect,
pic_state,
frame_context,
frame_state,
) {
return None;
@@ -1875,26 +1868,24 @@ impl PrimitiveStore {
// lookups ever show up in a profile).
let scroll_node = &frame_context
.clip_scroll_tree
.nodes[run.clip_and_scroll.scroll_node_id.0];
let clip_chain = frame_context
.clip_scroll_tree
.get_clip_chain(run.clip_and_scroll.clip_chain_index);
- if pic_context.perform_culling {
- if !scroll_node.invertible {
- debug!("{:?} {:?}: position not invertible", run.base_prim_index, pic_context.pipeline_id);
- continue;
- }
+ if !scroll_node.invertible {
+ debug!("{:?} {:?}: position not invertible", run.base_prim_index, pic_context.pipeline_id);
+ continue;
+ }
- if clip_chain.combined_outer_screen_rect.is_empty() {
- debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
- continue;
- }
+ if clip_chain.combined_outer_screen_rect.is_empty() {
+ debug!("{:?} {:?}: clipped out", run.base_prim_index, pic_context.pipeline_id);
+ continue;
}
let parent_relative_transform = pic_context
.inv_world_transform
.map(|inv_parent| {
inv_parent.pre_mul(&scroll_node.world_content_transform)
});
@@ -1905,19 +1896,20 @@ impl PrimitiveStore {
.nodes[original_reference_frame_index.0]
.world_content_transform;
parent.inverse()
.map(|inv_parent| {
inv_parent.pre_mul(&scroll_node.world_content_transform)
})
});
- let clip_chain_rect = match pic_context.perform_culling {
- true => get_local_clip_rect_for_nodes(scroll_node, clip_chain),
- false => None,
+ let clip_chain_rect = if pic_context.apply_local_clip_rect {
+ get_local_clip_rect_for_nodes(scroll_node, clip_chain)
+ } else {
+ None
};
let clip_chain_rect_index = match clip_chain_rect {
Some(rect) if rect.is_empty() => continue,
Some(rect) => {
frame_state.local_clip_rects.push(rect);
ClipChainRectIndex(frame_state.local_clip_rects.len() - 1)
}
--- a/gfx/webrender/src/record.rs
+++ b/gfx/webrender/src/record.rs
@@ -1,14 +1,14 @@
/* 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/. */
use api::{ApiMsg, FrameMsg, SceneMsg};
-use bincode::{serialize, Infinite};
+use bincode::serialize;
use byteorder::{LittleEndian, WriteBytesExt};
use std::any::TypeId;
use std::fmt::Debug;
use std::fs::File;
use std::io::Write;
use std::mem;
use std::path::PathBuf;
@@ -44,17 +44,17 @@ impl BinaryRecorder {
self.file.write_u32::<LittleEndian>(data.len() as u32).ok();
self.file.write(data).ok();
}
}
impl ApiRecordingReceiver for BinaryRecorder {
fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
if should_record_msg(msg) {
- let buf = serialize(msg, Infinite).unwrap();
+ let buf = serialize(msg).unwrap();
self.write_length_and_data(&buf);
}
}
fn write_payload(&mut self, _: u32, data: &[u8]) {
// signal payload with a 0 length
self.file.write_u32::<LittleEndian>(0).ok();
self.write_length_and_data(data);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -6,17 +6,16 @@ use api::{DeviceIntPoint, DeviceIntRect,
use api::{DeviceSize, PremultipliedColorF};
use box_shadow::{BoxShadowCacheKey};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::CoordinateSystemId;
use device::TextureFilter;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use gpu_types::{ImageSource, PictureType, RasterizationSpace};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
-use picture::ContentOrigin;
use prim_store::{PrimitiveIndex, ImageCacheKey};
#[cfg(feature = "debugger")]
use print_tree::{PrintTreePrinter};
use resource_cache::{CacheItem, ResourceCache};
use std::{cmp, ops, usize, f32, i32};
use texture_cache::{TextureCache, TextureCacheHandle};
use tiling::{RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind};
@@ -162,17 +161,17 @@ pub struct ClipRegionTask {
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PictureTask {
pub prim_index: PrimitiveIndex,
pub target_kind: RenderTargetKind,
- pub content_origin: ContentOrigin,
+ pub content_origin: DeviceIntPoint,
pub color: PremultipliedColorF,
pub pic_type: PictureType,
pub uv_rect_handle: GpuCacheHandle,
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -254,17 +253,17 @@ pub struct RenderTask {
pub saved_index: Option<SavedTargetIndex>,
}
impl RenderTask {
pub fn new_picture(
location: RenderTaskLocation,
prim_index: PrimitiveIndex,
target_kind: RenderTargetKind,
- content_origin: ContentOrigin,
+ content_origin: DeviceIntPoint,
color: PremultipliedColorF,
clear_mode: ClearMode,
children: Vec<RenderTaskId>,
pic_type: PictureType,
) -> Self {
RenderTask {
children,
location,
@@ -523,29 +522,21 @@ impl RenderTask {
// TODO(gw): Maybe there's a way to make this stuff a bit
// more type-safe. Although, it will always need
// to be kept in sync with the GLSL code anyway.
let (data1, data2) = match self.kind {
RenderTaskKind::Picture(ref task) => {
(
// Note: has to match `PICTURE_TYPE_*` in shaders
- // TODO(gw): Instead of using the sign of the picture
- // type here, we should consider encoding it
- // as a set of flags that get casted here
- // and in the shader. This is a bit tidier
- // and allows for future expansion of flags.
- match task.content_origin {
- ContentOrigin::Local(point) => [
- point.x, point.y, task.pic_type as u32 as f32,
- ],
- ContentOrigin::Screen(point) => [
- point.x as f32, point.y as f32, -(task.pic_type as u32 as f32),
- ],
- },
+ [
+ task.content_origin.x as f32,
+ task.content_origin.y as f32,
+ task.pic_type as u32 as f32,
+ ],
task.color.to_array()
)
}
RenderTaskKind::CacheMask(ref task) => {
(
[
task.actual_rect.origin.x as f32,
task.actual_rect.origin.y as f32,
@@ -916,16 +907,17 @@ impl RenderTaskCache {
// TODO(gw): Support color tasks in the texture cache,
// and perhaps consider if we can determine
// if some tasks are opaque as an optimization.
let descriptor = ImageDescriptor::new(
size.width as u32,
size.height as u32,
image_format,
is_opaque,
+ false,
);
// Allocate space in the texture cache, but don't supply
// and CPU-side data to be uploaded.
texture_cache.update(
&mut cache_entry.handle,
descriptor,
TextureFilter::Linear,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -37,17 +37,16 @@ use frame_builder::FrameBuilderConfig;
use gleam::gl;
use glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
use gpu_types::PrimitiveInstance;
use internal_types::{SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE, ResourceCacheError};
use internal_types::{CacheTextureId, DebugOutput, FastHashMap, RenderedDocument, ResultMsg};
use internal_types::{TextureUpdateList, TextureUpdateOp, TextureUpdateSource};
use internal_types::{RenderTargetInfo, SavedTargetIndex};
-use picture::ContentOrigin;
use prim_store::DeferredResolve;
use profiler::{BackendProfileCounters, FrameProfileCounters, Profiler};
use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
use query::{GpuProfiler, GpuTimer};
use rayon::{ThreadPool, ThreadPoolBuilder};
use record::ApiRecordingReceiver;
use render_backend::RenderBackend;
use scene_builder::SceneBuilder;
@@ -116,20 +115,16 @@ const GPU_TAG_BRUSH_SOLID: GpuProfileTag
const GPU_TAG_BRUSH_LINE: GpuProfileTag = GpuProfileTag {
label: "Line",
color: debug_colors::DARKRED,
};
const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
label: "C_Clip",
color: debug_colors::PURPLE,
};
-const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag {
- label: "C_TextRun",
- color: debug_colors::MISTYROSE,
-};
const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag {
label: "target init",
color: debug_colors::SLATEGREY,
};
const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag {
label: "data init",
color: debug_colors::LIGHTGREY,
};
@@ -1440,17 +1435,16 @@ impl Renderer {
(true, true) => FontRenderMode::Subpixel,
(true, false) => FontRenderMode::Alpha,
(false, _) => FontRenderMode::Mono,
};
let config = FrameBuilderConfig {
enable_scrollbars: options.enable_scrollbars,
default_font_render_mode,
- debug: options.debug,
dual_source_blending_is_enabled: true,
dual_source_blending_is_supported: ext_dual_source_blending,
};
let device_pixel_ratio = options.device_pixel_ratio;
// First set the flags to default and later call set_debug_flags to ensure any
// potential transition when enabling a flag is run.
let debug_flags = DebugFlags::default();
@@ -1479,16 +1473,17 @@ impl Renderer {
.build();
Arc::new(worker.unwrap())
});
let enable_render_on_scroll = options.enable_render_on_scroll;
let blob_image_renderer = options.blob_image_renderer.take();
let thread_listener_for_render_backend = thread_listener.clone();
let thread_listener_for_scene_builder = thread_listener.clone();
+ let renderer_id_for_render_backend = options.renderer_id.clone();
let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
let glyph_rasterizer = GlyphRasterizer::new(workers)?;
let (scene_builder, scene_tx, scene_rx) = SceneBuilder::new(config, api_tx.clone());
thread::Builder::new().name(scene_thread_name.clone()).spawn(move || {
register_thread_with_profiler(scene_thread_name.clone());
if let Some(ref thread_listener) = *thread_listener_for_scene_builder {
@@ -1501,16 +1496,17 @@ impl Renderer {
if let Some(ref thread_listener) = *thread_listener_for_scene_builder {
thread_listener.thread_stopped(&scene_thread_name);
}
})?;
thread::Builder::new().name(rb_thread_name.clone()).spawn(move || {
register_thread_with_profiler(rb_thread_name.clone());
if let Some(ref thread_listener) = *thread_listener_for_render_backend {
+ thread_listener.new_render_backend_thread(renderer_id_for_render_backend);
thread_listener.thread_started(&rb_thread_name);
}
let texture_cache = TextureCache::new(max_device_size);
let resource_cache = ResourceCache::new(
texture_cache,
glyph_rasterizer,
blob_image_renderer,
@@ -1718,17 +1714,17 @@ impl Renderer {
String::new()
}
#[cfg(feature = "debugger")]
fn get_screenshot_for_debugger(&mut self) -> String {
use api::ImageDescriptor;
- let desc = ImageDescriptor::new(1024, 768, ImageFormat::BGRA8, true);
+ let desc = ImageDescriptor::new(1024, 768, ImageFormat::BGRA8, true, false);
let data = self.device.read_pixels(&desc);
let screenshot = debug_server::Screenshot::new(desc.width, desc.height, data);
serde_json::to_string(&screenshot).unwrap()
}
#[cfg(not(feature = "debugger"))]
fn get_passes_for_debugger(&self) -> String {
@@ -1809,24 +1805,16 @@ impl Renderer {
);
debug_target.add(
debug_server::BatchKind::Cache,
"Horizontal Blur",
target.horizontal_blurs.len(),
);
for alpha_batch_container in &target.alpha_batch_containers {
- for (_, batch) in &alpha_batch_container.text_run_cache_prims {
- debug_target.add(
- debug_server::BatchKind::Cache,
- "Text Shadow",
- batch.len(),
- );
- }
-
for batch in alpha_batch_container
.opaque_batches
.iter()
.rev() {
debug_target.add(
debug_server::BatchKind::Opaque,
batch.key.kind.debug_name(),
batch.instances.len(),
@@ -2420,27 +2408,21 @@ impl Renderer {
.unwrap();
// Before submitting the composite batch, do the
// framebuffer readbacks that are needed for each
// composite operation in this batch.
let (readback_rect, readback_layer) = readback.get_target_rect();
let (backdrop_rect, _) = backdrop.get_target_rect();
let backdrop_screen_origin = match backdrop.kind {
- RenderTaskKind::Picture(ref task_info) => match task_info.content_origin {
- ContentOrigin::Local(_) => panic!("bug: composite from a local-space rasterized picture?"),
- ContentOrigin::Screen(p) => p,
- },
+ RenderTaskKind::Picture(ref task_info) => task_info.content_origin,
_ => panic!("bug: composite on non-picture?"),
};
let source_screen_origin = match source.kind {
- RenderTaskKind::Picture(ref task_info) => match task_info.content_origin {
- ContentOrigin::Local(_) => panic!("bug: composite from a local-space rasterized picture?"),
- ContentOrigin::Screen(p) => p,
- },
+ RenderTaskKind::Picture(ref task_info) => task_info.content_origin,
_ => panic!("bug: composite on non-picture?"),
};
// Bind the FBO to blit the backdrop to.
// Called per-instance in case the layer (and therefore FBO)
// changes. The device will skip the GL call if the requested
// target is already bound.
let cache_draw_target = (cache_texture, readback_layer.0 as i32);
@@ -2679,41 +2661,16 @@ impl Renderer {
&BatchTextures::no_texture(),
stats,
);
}
}
self.handle_scaling(render_tasks, &target.scalings, SourceTexture::CacheRGBA8);
- // Draw any textrun caches for this target. For now, this
- // is only used to cache text runs that are to be blurred
- // for shadow support. In the future it may be worth
- // considering using this for (some) other text runs, since
- // it removes the overhead of submitting many small glyphs
- // to multiple tiles in the normal text run case.
- for alpha_batch_container in &target.alpha_batch_containers {
- if !alpha_batch_container.text_run_cache_prims.is_empty() {
- self.device.set_blend(true);
- self.device.set_blend_mode_premultiplied_alpha();
-
- let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN);
- self.shaders.cs_text_run
- .bind(&mut self.device, projection, &mut self.renderer_errors);
- for (texture_id, instances) in &alpha_batch_container.text_run_cache_prims {
- self.draw_instanced_batch(
- instances,
- VertexArrayKind::Primitive,
- &BatchTextures::color(*texture_id),
- stats,
- );
- }
- }
- }
-
//TODO: record the pixel count for cached primitives
if target.needs_depth() {
let _gl = self.gpu_profile.start_marker("opaque batches");
let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
self.device.set_blend(false);
//Note: depth equality is needed for split planes
self.device.set_depth_func(DepthFunction::LessEqual);
@@ -3816,25 +3773,25 @@ pub trait ExternalImageHandler {
pub trait OutputImageHandler {
fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, DeviceIntSize)>;
fn unlock(&mut self, pipeline_id: PipelineId);
}
pub trait ThreadListener {
fn thread_started(&self, thread_name: &str);
fn thread_stopped(&self, thread_name: &str);
+ fn new_render_backend_thread(&self, renderer_id: Option<u64>);
}
pub struct RendererOptions {
pub device_pixel_ratio: f32,
pub resource_override_path: Option<PathBuf>,
pub enable_aa: bool,
pub enable_dithering: bool,
pub max_recorded_profiles: usize,
- pub debug: bool,
pub enable_scrollbars: bool,
pub precache_shaders: bool,
pub renderer_kind: RendererKind,
pub enable_subpixel_aa: bool,
pub clear_color: Option<ColorF>,
pub enable_clear_scissor: bool,
pub max_texture_size: Option<u32>,
pub scatter_gpu_cache_updates: bool,
@@ -3854,17 +3811,16 @@ impl Default for RendererOptions {
fn default() -> Self {
RendererOptions {
device_pixel_ratio: 1.0,
resource_override_path: None,
enable_aa: true,
enable_dithering: true,
debug_flags: DebugFlags::empty(),
max_recorded_profiles: 0,
- debug: false,
enable_scrollbars: false,
precache_shaders: false,
renderer_kind: RendererKind::Native,
enable_subpixel_aa: false,
clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
enable_clear_scissor: true,
max_texture_size: None,
// Scattered GPU cache updates haven't met a test that would show their superiority yet.
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -884,18 +884,17 @@ impl ResourceCache {
(Some(stride), offset)
};
ImageDescriptor {
width: actual_width,
height: actual_height,
stride,
offset,
- format: image_descriptor.format,
- is_opaque: image_descriptor.is_opaque,
+ ..*image_descriptor
}
} else {
image_template.descriptor.clone()
};
let filter = match request.rendering {
ImageRendering::Pixelated => {
TextureFilter::Nearest
@@ -903,17 +902,18 @@ impl ResourceCache {
ImageRendering::Auto | ImageRendering::CrispEdges => {
// If the texture uses linear filtering, enable mipmaps and
// trilinear filtering, for better image quality. We only
// support this for now on textures that are not placed
// into the shared cache. This accounts for any image
// that is > 512 in either dimension, so it should cover
// the most important use cases. We may want to support
// mip-maps on shared cache items in the future.
- if descriptor.width > 512 &&
+ if descriptor.allow_mipmaps &&
+ descriptor.width > 512 &&
descriptor.height > 512 &&
!self.texture_cache.is_allowed_in_shared_cache(
TextureFilter::Linear,
&descriptor,
) {
TextureFilter::Trilinear
} else {
TextureFilter::Linear
@@ -949,16 +949,19 @@ impl ResourceCache {
self.cached_glyphs.clear();
}
if what.contains(ClearCache::GLYPH_DIMENSIONS) {
self.cached_glyph_dimensions.clear();
}
if what.contains(ClearCache::RENDER_TASKS) {
self.cached_render_tasks.clear();
}
+ if what.contains(ClearCache::TEXTURE_CACHE) {
+ self.texture_cache.clear();
+ }
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) {
self.resources
.image_templates
.images
.retain(|key, _| key.0 != namespace);
self.cached_images
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -408,17 +408,16 @@ fn create_clip_shader(name: &'static str
program
}
pub struct Shaders {
// These are "cache shaders". These shaders are used to
// draw intermediate results to cache targets. The results
// of these shaders are then used by the primitive shaders.
- pub cs_text_run: LazilyCompiledShader,
pub cs_blur_a8: LazilyCompiledShader,
pub cs_blur_rgba8: LazilyCompiledShader,
// Brush shaders
brush_solid: BrushShader,
brush_line: BrushShader,
brush_image: Vec<Option<BrushShader>>,
brush_blend: BrushShader,
@@ -453,24 +452,16 @@ pub struct Shaders {
}
impl Shaders {
pub fn new(
device: &mut Device,
gl_type: GlType,
options: &RendererOptions,
) -> Result<Self, ShaderError> {
- let cs_text_run = LazilyCompiledShader::new(
- ShaderKind::Cache(VertexArrayKind::Primitive),
- "cs_text_run",
- &[],
- device,
- options.precache_shaders,
- )?;
-
let brush_solid = BrushShader::new(
"brush_solid",
device,
&[],
options.precache_shaders,
)?;
let brush_line = BrushShader::new(
@@ -677,17 +668,16 @@ impl Shaders {
ShaderKind::Primitive,
"ps_split_composite",
&[],
device,
options.precache_shaders,
)?;
Ok(Shaders {
- cs_text_run,
cs_blur_a8,
cs_blur_rgba8,
brush_solid,
brush_line,
brush_image,
brush_blend,
brush_mix_blend,
brush_yuv_image,
@@ -777,17 +767,16 @@ impl Shaders {
}
};
prim_shader.get(transform_kind)
}
}
}
pub fn deinit(self, device: &mut Device) {
- self.cs_text_run.deinit(device);
self.cs_blur_a8.deinit(device);
self.cs_blur_rgba8.deinit(device);
self.brush_solid.deinit(device);
self.brush_line.deinit(device);
self.brush_blend.deinit(device);
self.brush_mix_blend.deinit(device);
self.brush_radial_gradient.deinit(device);
self.brush_linear_gradient.deinit(device);
--- a/gfx/webrender/src/texture_cache.rs
+++ b/gfx/webrender/src/texture_cache.rs
@@ -276,16 +276,66 @@ impl TextureCache {
pending_updates: TextureUpdateList::new(),
frame_id: FrameId(0),
entries: FreeList::new(),
standalone_entry_handles: Vec::new(),
shared_entry_handles: Vec::new(),
}
}
+ pub fn clear(&mut self) {
+ let standalone_entry_handles = mem::replace(
+ &mut self.standalone_entry_handles,
+ Vec::new(),
+ );
+
+ for handle in standalone_entry_handles {
+ let entry = self.entries.free(handle);
+ entry.evict();
+ self.free(entry);
+ }
+
+ let shared_entry_handles = mem::replace(
+ &mut self.shared_entry_handles,
+ Vec::new(),
+ );
+
+ for handle in shared_entry_handles {
+ let entry = self.entries.free(handle);
+ entry.evict();
+ self.free(entry);
+ }
+
+ assert!(self.entries.len() == 0);
+
+ if let Some(texture_id) = self.array_a8_linear.clear() {
+ self.pending_updates.push(TextureUpdate {
+ id: texture_id,
+ op: TextureUpdateOp::Free,
+ });
+ self.cache_textures.free(texture_id, self.array_a8_linear.format);
+ }
+
+ if let Some(texture_id) = self.array_rgba8_linear.clear() {
+ self.pending_updates.push(TextureUpdate {
+ id: texture_id,
+ op: TextureUpdateOp::Free,
+ });
+ self.cache_textures.free(texture_id, self.array_rgba8_linear.format);
+ }
+
+ if let Some(texture_id) = self.array_rgba8_nearest.clear() {
+ self.pending_updates.push(TextureUpdate {
+ id: texture_id,
+ op: TextureUpdateOp::Free,
+ });
+ self.cache_textures.free(texture_id, self.array_rgba8_nearest.format);
+ }
+ }
+
pub fn begin_frame(&mut self, frame_id: FrameId) {
self.frame_id = frame_id;
}
pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
self.expire_old_standalone_entries();
self.array_a8_linear
@@ -1008,16 +1058,22 @@ impl TextureArray {
filter,
layer_count,
is_allocated: false,
regions: Vec::new(),
texture_id: None,
}
}
+ fn clear(&mut self) -> Option<CacheTextureId> {
+ self.is_allocated = false;
+ self.regions.clear();
+ self.texture_id.take()
+ }
+
fn update_profile(&self, counter: &mut ResourceProfileCounter) {
if self.is_allocated {
let size = self.layer_count as u32 * TEXTURE_LAYER_DIMENSIONS *
TEXTURE_LAYER_DIMENSIONS * self.format.bytes_per_pixel();
counter.set(self.layer_count as usize, size as usize);
} else {
counter.set(0, 0);
}
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -40,20 +40,16 @@ const SHADERS: &[Shader] = &[
name: "cs_clip_border",
features: CLIP_FEATURES,
},
// Cache shaders
Shader {
name: "cs_blur",
features: CACHE_FEATURES,
},
- Shader {
- name: "cs_text_run",
- features: CACHE_FEATURES,
- },
// Prim shaders
Shader {
name: "ps_border_corner",
features: PRIM_FEATURES,
},
Shader {
name: "ps_border_edge",
features: PRIM_FEATURES,
--- a/gfx/webrender_api/Cargo.toml
+++ b/gfx/webrender_api/Cargo.toml
@@ -8,21 +8,21 @@ repository = "https://github.com/servo/w
[features]
nightly = ["euclid/unstable", "serde/unstable"]
ipc = ["ipc-channel"]
serialize = []
deserialize = []
[dependencies]
app_units = "0.6"
+bincode = "1.0"
bitflags = "1.0"
-bincode = "0.9"
byteorder = "1.2.1"
+ipc-channel = {version = "0.10.0", optional = true}
euclid = { version = "0.17", features = ["serde"] }
-ipc-channel = {version = "0.9", optional = true}
serde = { version = "=1.0.27", features = ["rc"] }
serde_derive = { version = "=1.0.27", features = ["deserialize_in_place"] }
time = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
core-graphics = "0.13"
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -531,16 +531,17 @@ bitflags!{
bitflags!{
/// Mask for clearing caches in debug commands.
#[derive(Deserialize, Serialize)]
pub struct ClearCache: u8 {
const IMAGES = 0x1;
const GLYPHS = 0x2;
const GLYPH_DIMENSIONS = 0x4;
const RENDER_TASKS = 0x8;
+ const TEXTURE_CACHE = 0x16;
}
}
/// Information about a loaded capture of each document
/// that is returned by `RenderBackend`.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CapturedDocument {
pub document_id: DocumentId,
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -415,18 +415,18 @@ pub struct GradientStop {
pub offset: f32,
pub color: ColorF,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct RadialGradient {
pub center: LayoutPoint,
pub radius: LayoutSize,
- pub start_radius: f32,
- pub end_radius: f32,
+ pub start_offset: f32,
+ pub end_offset: f32,
pub extend_mode: ExtendMode,
} // IMPLICIT stops: Vec<GradientStop>
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ClipChainItem {
pub id: ClipChainId,
pub parent: Option<ClipChainId>,
} // IMPLICIT stops: Vec<ClipId>
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -157,24 +157,24 @@ impl BuiltDisplayList {
AuxIter::new(&self.data[range.start .. range.start + range.length])
}
}
/// Returns the byte-range the slice occupied, and the number of elements
/// in the slice.
fn skip_slice<T: for<'de> Deserialize<'de>>(
list: &BuiltDisplayList,
- data: &mut &[u8],
+ mut data: &mut &[u8],
) -> (ItemRange<T>, usize) {
let base = list.data.as_ptr() as usize;
- let byte_size: usize = bincode::deserialize_from(data, bincode::Infinite)
+ let byte_size: usize = bincode::deserialize_from(&mut data)
.expect("MEH: malicious input?");
let start = data.as_ptr() as usize;
- let item_count: usize = bincode::deserialize_from(data, bincode::Infinite)
+ let item_count: usize = bincode::deserialize_from(&mut data)
.expect("MEH: malicious input?");
// Remember how many bytes item_count occupied
let item_count_size = data.as_ptr() as usize - start;
let range = ItemRange {
start: start - base, // byte offset to item_count
length: byte_size + item_count_size, // number of bytes for item_count + payload
@@ -236,19 +236,18 @@ impl<'a> BuiltDisplayListIter<'a> {
self.cur_clip_chain_items = ItemRange::default();
loop {
if self.data.len() == 0 {
return None;
}
{
- let reader = bincode::read_types::IoReader::new(UnsafeReader::new(&mut self.data));
- let mut deserializer = bincode::Deserializer::new(reader, bincode::Infinite);
- Deserialize::deserialize_in_place(&mut deserializer, &mut self.cur_item)
+ let reader = bincode::IoReader::new(UnsafeReader::new(&mut self.data));
+ bincode::deserialize_in_place(reader, &mut self.cur_item)
.expect("MEH: malicious process?");
}
match self.cur_item.item {
SetGradientStops => {
self.cur_stops = skip_slice::<GradientStop>(self.list, &mut self.data).0;
// This is a dummy item, skip over it
@@ -388,18 +387,17 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
}
}
impl<'de, 'a, T: Deserialize<'de>> AuxIter<'a, T> {
pub fn new(mut data: &'a [u8]) -> Self {
let size: usize = if data.len() == 0 {
0 // Accept empty ItemRanges pointing anywhere
} else {
- bincode::deserialize_from(&mut UnsafeReader::new(&mut data), bincode::Infinite)
- .expect("MEH: malicious input?")
+ bincode::deserialize_from(&mut UnsafeReader::new(&mut data)).expect("MEH: malicious input?")
};
AuxIter {
data,
size,
_boo: PhantomData,
}
}
@@ -409,17 +407,17 @@ impl<'a, T: for<'de> Deserialize<'de>> I
type Item = T;
fn next(&mut self) -> Option<T> {
if self.size == 0 {
None
} else {
self.size -= 1;
Some(
- bincode::deserialize_from(&mut UnsafeReader::new(&mut self.data), bincode::Infinite)
+ bincode::deserialize_from(&mut UnsafeReader::new(&mut self.data))
.expect("MEH: malicious input?"),
)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.size, Some(self.size))
}
@@ -660,23 +658,23 @@ impl<'a> Write for SizeCounter {
/// invocations.
///
/// If this assumption is incorrect, the result will be Undefined Behaviour. This
/// assumption should hold for all derived Serialize impls, which is all we currently
/// use.
fn serialize_fast<T: Serialize>(vec: &mut Vec<u8>, e: &T) {
// manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
let mut size = SizeCounter(0);
- bincode::serialize_into(&mut size, e, bincode::Infinite).unwrap();
+ bincode::serialize_into(&mut size, e).unwrap();
vec.reserve(size.0);
let old_len = vec.len();
let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) };
let mut w = UnsafeVecWriter(ptr);
- bincode::serialize_into(&mut w, e, bincode::Infinite).unwrap();
+ bincode::serialize_into(&mut w, e).unwrap();
// fix up the length
unsafe { vec.set_len(old_len + size.0); }
// make sure we wrote the right amount
debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
}
@@ -697,29 +695,29 @@ fn serialize_iter_fast<I>(vec: &mut Vec<
where I: ExactSizeIterator + Clone,
I::Item: Serialize,
{
// manually counting the size is faster than vec.reserve(bincode::serialized_size(&e) as usize) for some reason
let mut size = SizeCounter(0);
let mut count1 = 0;
for e in iter.clone() {
- bincode::serialize_into(&mut size, &e, bincode::Infinite).unwrap();
+ bincode::serialize_into(&mut size, &e).unwrap();
count1 += 1;
}
vec.reserve(size.0);
let old_len = vec.len();
let ptr = unsafe { vec.as_mut_ptr().offset(old_len as isize) };
let mut w = UnsafeVecWriter(ptr);
let mut count2 = 0;
for e in iter {
- bincode::serialize_into(&mut w, &e, bincode::Infinite).unwrap();
+ bincode::serialize_into(&mut w, &e).unwrap();
count2 += 1;
}
// fix up the length
unsafe { vec.set_len(old_len + size.0); }
// make sure we wrote the right amount
debug_assert_eq!(((w.0 as usize) - (vec.as_ptr() as usize)), vec.len());
@@ -957,17 +955,16 @@ impl DisplayListBuilder {
// Now write the actual byte_size
let final_offset = data.len();
let byte_size = final_offset - payload_offset;
// Note we don't use serialize_fast because we don't want to change the Vec's len
bincode::serialize_into(
&mut &mut data[byte_size_offset..],
&byte_size,
- bincode::Infinite,
).unwrap();
debug_assert_eq!(len, count);
}
fn push_iter<I>(&mut self, iter: I)
where
I: IntoIterator,
@@ -1070,70 +1067,51 @@ impl DisplayListBuilder {
fn normalize_stops(stops: &mut Vec<GradientStop>, extend_mode: ExtendMode) -> (f32, f32) {
assert!(stops.len() >= 2);
let first = *stops.first().unwrap();
let last = *stops.last().unwrap();
assert!(first.offset <= last.offset);
- let stops_origin = first.offset;
let stops_delta = last.offset - first.offset;
if stops_delta > 0.000001 {
for stop in stops {
- stop.offset = (stop.offset - stops_origin) / stops_delta;
+ stop.offset = (stop.offset - first.offset) / stops_delta;
}
(first.offset, last.offset)
} else {
// We have a degenerate gradient and can't accurately transform the stops
// what happens here depends on the repeat behavior, but in any case
// we reconstruct the gradient stops to something simpler and equivalent
stops.clear();
match extend_mode {
ExtendMode::Clamp => {
// This gradient is two colors split at the offset of the stops,
// so create a gradient with two colors split at 0.5 and adjust
// the gradient line so 0.5 is at the offset of the stops
- stops.push(GradientStop {
- color: first.color,
- offset: 0.0,
- });
- stops.push(GradientStop {
- color: first.color,
- offset: 0.5,
- });
- stops.push(GradientStop {
- color: last.color,
- offset: 0.5,
- });
- stops.push(GradientStop {
- color: last.color,
- offset: 1.0,
- });
+ stops.push(GradientStop { color: first.color, offset: 0.0, });
+ stops.push(GradientStop { color: first.color, offset: 0.5, });
+ stops.push(GradientStop { color: last.color, offset: 0.5, });
+ stops.push(GradientStop { color: last.color, offset: 1.0, });
let offset = last.offset;
(offset - 0.5, offset + 0.5)
}
ExtendMode::Repeat => {
// A repeating gradient with stops that are all in the same
// position should just display the last color. I believe the
// spec says that it should be the average color of the gradient,
// but this matches what Gecko and Blink does
- stops.push(GradientStop {
- color: last.color,
- offset: 0.0,
- });
- stops.push(GradientStop {
- color: last.color,
- offset: 1.0,
- });
+ stops.push(GradientStop { color: last.color, offset: 0.0, });
+ stops.push(GradientStop { color: last.color, offset: 1.0, });
(0.0, 1.0)
}
}
}
}
// NOTE: gradients must be pushed in the order they're created
@@ -1170,69 +1148,41 @@ impl DisplayListBuilder {
) -> RadialGradient {
if radius.width <= 0.0 || radius.height <= 0.0 {
// The shader cannot handle a non positive radius. So
// reuse the stops vector and construct an equivalent
// gradient.
let last_color = stops.last().unwrap().color;
let stops = [
- GradientStop {
- offset: 0.0,
- color: last_color,
- },
- GradientStop {
- offset: 1.0,
- color: last_color,
- },
+ GradientStop { offset: 0.0, color: last_color, },
+ GradientStop { offset: 1.0, color: last_color, },
];
self.push_stops(&stops);
return RadialGradient {
center,
radius: LayoutSize::new(1.0, 1.0),
- start_radius: 0.0,
- end_radius: 1.0,
+ start_offset: 0.0,
+ end_offset: 1.0,
extend_mode,
};
}
let (start_offset, end_offset) =
DisplayListBuilder::normalize_stops(&mut stops, extend_mode);
self.push_stops(&stops);
RadialGradient {
center,
radius,
- start_radius: radius.width * start_offset,
- end_radius: radius.width * end_offset,
- extend_mode,
- }
- }
-
- // NOTE: gradients must be pushed in the order they're created
- // because create_gradient stores the stops in anticipation
- pub fn create_complex_radial_gradient(
- &mut self,
- center: LayoutPoint,
- radius: LayoutSize,
- start_radius: f32,
- end_radius: f32,
- stops: Vec<GradientStop>,
- extend_mode: ExtendMode,
- ) -> RadialGradient {
- self.push_stops(&stops);
-
- RadialGradient {
- center,
- radius,
- start_radius,
- end_radius,
+ start_offset: start_offset,
+ end_offset: end_offset,
extend_mode,
}
}
pub fn push_border(
&mut self,
info: &LayoutPrimitiveInfo,
widths: BorderWidths,
--- a/gfx/webrender_api/src/image.rs
+++ b/gfx/webrender_api/src/image.rs
@@ -71,27 +71,35 @@ impl ImageFormat {
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ImageDescriptor {
pub format: ImageFormat,
pub width: u32,
pub height: u32,
pub stride: Option<u32>,
pub offset: u32,
pub is_opaque: bool,
+ pub allow_mipmaps: bool,
}
impl ImageDescriptor {
- pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
+ pub fn new(
+ width: u32,
+ height: u32,
+ format: ImageFormat,
+ is_opaque: bool,
+ allow_mipmaps: bool,
+ ) -> Self {
ImageDescriptor {
width,
height,
format,
stride: None,
offset: 0,
is_opaque,
+ allow_mipmaps,
}
}
pub fn compute_stride(&self) -> u32 {
self.stride
.unwrap_or(self.width * self.format.bytes_per_pixel())
}
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-2083e83d958dd4a230ccae5c518e4bc8fbf88009
+30cfecc343e407ce277d07cf09f27ad9dd1917a1
--- a/gfx/wrench/Cargo.toml
+++ b/gfx/wrench/Cargo.toml
@@ -2,17 +2,17 @@
name = "wrench"
version = "0.3.0"
authors = ["Vladimir Vukicevic <vladimir@pobox.com>"]
build = "build.rs"
license = "MPL-2.0"
[dependencies]
base64 = "0.6"
-bincode = "0.9"
+bincode = "1.0"
byteorder = "1.0"
env_logger = { version = "0.5", optional = true }
euclid = "0.17"
gleam = "0.4"
glutin = "0.12"
app_units = "0.6"
image = "0.18"
clap = { version = "2", features = ["yaml"] }
--- a/gfx/wrench/src/args.yaml
+++ b/gfx/wrench/src/args.yaml
@@ -12,20 +12,16 @@ args:
short: v
long: verbose
help: Enable verbose display
- zoom:
short: z
long: zoom
help: Set zoom factor
takes_value: true
- - debug:
- short: d
- long: debug
- help: Enable debug renderer
- shaders:
long: shaders
help: Override path for shaders
takes_value: true
- rebuild:
short: r
long: rebuild
help: Rebuild display list from scratch every frame
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -424,17 +424,16 @@ fn main() {
&mut window,
events_loop.as_mut().map(|el| el.create_proxy()),
res_path,
dp_ratio,
save_type,
dim,
args.is_present("rebuild"),
args.is_present("no_subpixel_aa"),
- args.is_present("debug"),
args.is_present("verbose"),
args.is_present("no_scissor"),
args.is_present("no_batch"),
args.is_present("precache"),
args.is_present("slow_subpixel"),
zoom_factor.unwrap_or(1.0),
notifier,
);
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -83,17 +83,17 @@ impl<'a> RawtestHarness<'a> {
println!("\ttile decomposition...");
// This exposes a crash in tile decomposition
let layout_size = LayoutSize::new(800., 800.);
let mut resources = ResourceUpdates::new();
let blob_img = self.wrench.api.generate_image_key();
resources.add_image(
blob_img,
- ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(151, 56, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(128),
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(448.899994, 74.0, 151.000031, 56.));
@@ -135,17 +135,17 @@ impl<'a> RawtestHarness<'a> {
let layout_size = LayoutSize::new(400., 400.);
let mut resources = ResourceUpdates::new();
{
let api = &self.wrench.api;
blob_img = api.generate_image_key();
resources.add_image(
blob_img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
}
// draw the blob the first time
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
@@ -215,24 +215,24 @@ impl<'a> RawtestHarness<'a> {
let layout_size = LayoutSize::new(400., 400.);
let mut resources = ResourceUpdates::new();
let (blob_img, blob_img2) = {
let api = &self.wrench.api;
blob_img = api.generate_image_key();
resources.add_image(
blob_img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
blob_img2 = api.generate_image_key();
resources.add_image(
blob_img2,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(80, 50, 150, 255))),
None,
);
(blob_img, blob_img2)
};
// setup some counters to count how many times each image is requested
let img1_requested = Arc::new(AtomicIsize::new(0));
@@ -280,38 +280,38 @@ impl<'a> RawtestHarness<'a> {
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
let _pixels_first = self.render_and_get_pixels(window_rect);
// update and redraw both images
let mut resources = ResourceUpdates::new();
resources.update_image(
blob_img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
resources.update_image(
blob_img2,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(59, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
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));
let _pixels_second = self.render_and_get_pixels(window_rect);
// only update the first image
let mut resources = ResourceUpdates::new();
resources.update_image(
blob_img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))),
Some(rect(200, 200, 100, 100)),
);
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));
let _pixels_third = self.render_and_get_pixels(window_rect);
@@ -334,17 +334,17 @@ impl<'a> RawtestHarness<'a> {
);
let layout_size = LayoutSize::new(400., 400.);
let mut resources = ResourceUpdates::new();
let blob_img = {
let img = self.wrench.api.generate_image_key();
resources.add_image(
img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
img
};
// draw the blobs the first time
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
@@ -363,17 +363,17 @@ impl<'a> RawtestHarness<'a> {
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
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(
blob_img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(rect(100, 100, 100, 100)),
);
// make a new display list that refers to the first image
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
builder.push_image(
@@ -387,17 +387,17 @@ impl<'a> RawtestHarness<'a> {
self.submit_dl(&mut epoch, layout_size, builder, Some(resources));
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(
blob_img,
- ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(500, 500, ImageFormat::BGRA8, true, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 150, 150, 255))),
Some(rect(200, 200, 100, 100)),
);
// make a new display list that refers to the first image
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
builder.push_image(
@@ -502,17 +502,17 @@ impl<'a> RawtestHarness<'a> {
);
// 1. render some scene
let mut resources = ResourceUpdates::new();
let image = self.wrench.api.generate_image_key();
resources.add_image(
image,
- ImageDescriptor::new(1, 1, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(1, 1, ImageFormat::BGRA8, true, false),
ImageData::new(vec![0xFF, 0, 0, 0xFF]),
None,
);
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
builder.push_image(
&LayoutPrimitiveInfo::new(rect(300.0, 70.0, 150.0, 50.0)),
--- a/gfx/wrench/src/reftest.rs
+++ b/gfx/wrench/src/reftest.rs
@@ -27,16 +27,17 @@ const PLATFORM: &str = "linux";
#[cfg(target_os = "macos")]
const PLATFORM: &str = "mac";
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
const PLATFORM: &str = "other";
const OPTION_DISABLE_SUBPX: &str = "disable-subpixel";
const OPTION_DISABLE_AA: &str = "disable-aa";
const OPTION_DISABLE_DUAL_SOURCE_BLENDING: &str = "disable-dual-source-blending";
+const OPTION_ALLOW_MIPMAPS: &str = "allow-mipmaps";
pub struct ReftestOptions {
// These override values that are lower.
pub allow_max_difference: usize,
pub allow_num_differences: usize,
}
impl ReftestOptions {
@@ -72,16 +73,17 @@ pub struct Reftest {
reference: PathBuf,
font_render_mode: Option<FontRenderMode>,
max_difference: usize,
num_differences: usize,
expected_draw_calls: Option<usize>,
expected_alpha_targets: Option<usize>,
expected_color_targets: Option<usize>,
disable_dual_source_blending: bool,
+ allow_mipmaps: bool,
zoom_factor: f32,
}
impl Display for Reftest {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(
f,
"{} {} {}",
@@ -191,16 +193,17 @@ impl ReftestManifest {
let mut max_count = 0;
let mut op = ReftestOp::Equal;
let mut font_render_mode = None;
let mut expected_color_targets = None;
let mut expected_alpha_targets = None;
let mut expected_draw_calls = None;
let mut disable_dual_source_blending = false;
let mut zoom_factor = 1.0;
+ let mut allow_mipmaps = false;
for (i, token) in tokens.iter().enumerate() {
match *token {
"include" => {
assert!(i == 0, "include must be by itself");
let include = dir.join(tokens[1]);
reftests.append(
@@ -243,16 +246,19 @@ impl ReftestManifest {
font_render_mode = Some(FontRenderMode::Alpha);
}
if args.iter().any(|arg| arg == &OPTION_DISABLE_AA) {
font_render_mode = Some(FontRenderMode::Mono);
}
if args.iter().any(|arg| arg == &OPTION_DISABLE_DUAL_SOURCE_BLENDING) {
disable_dual_source_blending = true;
}
+ if args.iter().any(|arg| arg == &OPTION_ALLOW_MIPMAPS) {
+ allow_mipmaps = true;
+ }
}
"==" => {
op = ReftestOp::Equal;
}
"!=" => {
op = ReftestOp::NotEqual;
}
_ => {
@@ -262,16 +268,17 @@ impl ReftestManifest {
reference: dir.join(tokens[i + 1]),
font_render_mode,
max_difference: cmp::max(max_difference, options.allow_max_difference),
num_differences: cmp::max(max_count, options.allow_num_differences),
expected_draw_calls,
expected_alpha_targets,
expected_color_targets,
disable_dual_source_blending,
+ allow_mipmaps,
zoom_factor,
});
break;
}
}
}
}
@@ -329,16 +336,22 @@ impl<'a> ReftestHarness<'a> {
}
failing.len()
}
fn run_reftest(&mut self, t: &Reftest) -> bool {
println!("REFTEST {}", t);
+ self.wrench
+ .api
+ .send_debug_cmd(
+ DebugCommand::ClearCaches(ClearCache::all())
+ );
+
self.wrench.set_page_zoom(ZoomFactor::new(t.zoom_factor));
if t.disable_dual_source_blending {
self.wrench
.api
.send_debug_cmd(
DebugCommand::EnableDualSourceBlending(false)
);
@@ -346,31 +359,33 @@ impl<'a> ReftestHarness<'a> {
let window_size = self.window.get_inner_size();
let reference = match t.reference.extension().unwrap().to_str().unwrap() {
"yaml" => {
let (reference, _) = self.render_yaml(
t.reference.as_path(),
window_size,
t.font_render_mode,
+ t.allow_mipmaps,
);
reference
}
"png" => {
self.load_image(t.reference.as_path(), ImageFormat::PNG)
}
other => panic!("Unknown reftest extension: {}", other),
};
// the reference can be smaller than the window size,
// in which case we only compare the intersection
let (test, stats) = self.render_yaml(
t.test.as_path(),
reference.size,
t.font_render_mode,
+ t.allow_mipmaps,
);
if t.disable_dual_source_blending {
self.wrench
.api
.send_debug_cmd(
DebugCommand::EnableDualSourceBlending(true)
);
@@ -459,19 +474,21 @@ impl<'a> ReftestHarness<'a> {
}
}
fn render_yaml(
&mut self,
filename: &Path,
size: DeviceUintSize,
font_render_mode: Option<FontRenderMode>,
+ allow_mipmaps: bool,
) -> (ReftestImage, RendererStats) {
let mut reader = YamlFrameReader::new(filename);
reader.set_font_render_mode(font_render_mode);
+ reader.allow_mipmaps(allow_mipmaps);
reader.do_frame(self.wrench);
// wait for the frame
self.rx.recv().unwrap();
let stats = self.wrench.render();
let window_size = self.window.get_inner_size();
assert!(size.width <= window_size.width && size.height <= window_size.height);
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -169,17 +169,16 @@ impl Wrench {
window: &mut WindowWrapper,
proxy: Option<EventsLoopProxy>,
shader_override_path: Option<PathBuf>,
dp_ratio: f32,
save_type: Option<SaveType>,
size: DeviceUintSize,
do_rebuild: bool,
no_subpixel_aa: bool,
- debug: bool,
verbose: bool,
no_scissor: bool,
no_batch: bool,
precache_shaders: bool,
disable_dual_source_blending: bool,
zoom_factor: f32,
notifier: Option<Box<RenderNotifier>>,
) -> Self {
@@ -202,17 +201,16 @@ impl Wrench {
debug_flags.set(DebugFlags::DISABLE_BATCHING, no_batch);
let callbacks = Arc::new(Mutex::new(blob::BlobCallbacks::new()));
let opts = webrender::RendererOptions {
device_pixel_ratio: dp_ratio,
resource_override_path: shader_override_path,
recorder,
enable_subpixel_aa: !no_subpixel_aa,
- debug,
debug_flags,
enable_clear_scissor: !no_scissor,
max_recorded_profiles: 16,
precache_shaders,
blob_image_renderer: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))),
disable_dual_source_blending,
..Default::default()
};
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -99,17 +99,17 @@ fn generate_checkerboard_image(
pixels.push(value);
pixels.push(value);
pixels.push(0xff);
}
}
}
(
- ImageDescriptor::new(width, height, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(width, height, ImageFormat::BGRA8, true, false),
ImageData::new(pixels),
)
}
fn generate_xy_gradient_image(w: u32, h: u32) -> (ImageDescriptor, ImageData) {
let mut pixels = Vec::with_capacity((w * h * 4) as usize);
for y in 0 .. h {
for x in 0 .. w {
@@ -117,17 +117,17 @@ fn generate_xy_gradient_image(w: u32, h:
pixels.push((y as f32 / h as f32 * 255.0 * grid) as u8);
pixels.push(0);
pixels.push((x as f32 / w as f32 * 255.0 * grid) as u8);
pixels.push(255);
}
}
(
- ImageDescriptor::new(w, h, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(w, h, ImageFormat::BGRA8, true, false),
ImageData::new(pixels),
)
}
fn generate_solid_color_image(
r: u8,
g: u8,
b: u8,
@@ -147,17 +147,17 @@ fn generate_solid_color_image(
let end = ptr.offset((w * h) as isize);
while ptr < end {
*ptr = color;
ptr = ptr.offset(1);
}
}
(
- ImageDescriptor::new(w, h, ImageFormat::BGRA8, a == 255),
+ ImageDescriptor::new(w, h, ImageFormat::BGRA8, a == 255, false),
ImageData::new(pixels),
)
}
fn is_image_opaque(format: ImageFormat, bytes: &[u8]) -> bool {
match format {
@@ -195,16 +195,17 @@ pub struct YamlFrameReader {
/// scroll layers should be initialized with.
scroll_offsets: HashMap<ExternalScrollId, LayerPoint>,
image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>,
fonts: HashMap<FontDescriptor, FontKey>,
font_instances: HashMap<(FontKey, Au, FontInstanceFlags), FontInstanceKey>,
font_render_mode: Option<FontRenderMode>,
+ allow_mipmaps: bool,
/// A HashMap that allows specifying a numeric id for clip and clip chains in YAML
/// and having each of those ids correspond to a unique ClipId.
clip_id_map: HashMap<u64, ClipId>,
}
impl YamlFrameReader {
pub fn new(yaml_path: &Path) -> YamlFrameReader {
@@ -219,16 +220,17 @@ impl YamlFrameReader {
queue_depth: 1,
include_only: vec![],
scroll_offsets: HashMap::new(),
fonts: HashMap::new(),
font_instances: HashMap::new(),
font_render_mode: None,
image_map: HashMap::new(),
clip_id_map: HashMap::new(),
+ allow_mipmaps: false,
}
}
pub fn deinit(mut self, wrench: &mut Wrench) {
let mut updates = ResourceUpdates::new();
for (_, font_instance) in self.font_instances.drain() {
updates.delete_font_instance(font_instance);
@@ -410,16 +412,17 @@ impl YamlFrameReader {
}
_ => panic!("We don't support whatever your crazy image type is, come on"),
};
let descriptor = ImageDescriptor::new(
image_dims.0,
image_dims.1,
format,
is_image_opaque(format, &bytes[..]),
+ self.allow_mipmaps,
);
let data = ImageData::new(bytes);
(descriptor, data)
}
_ => {
// This is a hack but it is convenient when generating test cases and avoids
// bloating the repository.
match parse_function(
@@ -511,16 +514,20 @@ impl YamlFrameReader {
ref family,
weight,
style,
stretch,
} => wrench.font_key_from_properties(family, weight, style, stretch),
})
}
+ pub fn allow_mipmaps(&mut self, allow_mipmaps: bool) {
+ self.allow_mipmaps = allow_mipmaps;
+ }
+
pub fn set_font_render_mode(&mut self, render_mode: Option<FontRenderMode>) {
self.font_render_mode = render_mode;
}
fn get_or_create_font_instance(
&mut self,
font_key: FontKey,
size: Au,
@@ -596,77 +603,38 @@ impl YamlFrameReader {
};
dl.create_gradient(start, end, stops, extend_mode)
}
fn to_radial_gradient(&mut self, dl: &mut DisplayListBuilder, item: &Yaml) -> RadialGradient {
let center = item["center"].as_point().expect("radial gradient must have center");
let radius = item["radius"].as_size().expect("radial gradient must have a radius");
-
- if item["start-radius"].is_badvalue() {
- let stops = item["stops"]
- .as_vec()
- .expect("radial gradient must have stops")
- .chunks(2)
- .map(|chunk| {
- GradientStop {
- offset: chunk[0]
- .as_force_f32()
- .expect("gradient stop offset is not f32"),
- color: chunk[1]
- .as_colorf()
- .expect("gradient stop color is not color"),
- }
- })
- .collect::<Vec<_>>();
- let extend_mode = if item["repeat"].as_bool().unwrap_or(false) {
- ExtendMode::Repeat
- } else {
- ExtendMode::Clamp
- };
-
- dl.create_radial_gradient(center, radius, stops, extend_mode)
+ let stops = item["stops"]
+ .as_vec()
+ .expect("radial gradient must have stops")
+ .chunks(2)
+ .map(|chunk| {
+ GradientStop {
+ offset: chunk[0]
+ .as_force_f32()
+ .expect("gradient stop offset is not f32"),
+ color: chunk[1]
+ .as_colorf()
+ .expect("gradient stop color is not color"),
+ }
+ })
+ .collect::<Vec<_>>();
+ let extend_mode = if item["repeat"].as_bool().unwrap_or(false) {
+ ExtendMode::Repeat
} else {
- let start_radius = item["start-radius"]
- .as_force_f32()
- .expect("radial gradient must have start radius");
- let end_radius = item["end-radius"]
- .as_force_f32()
- .expect("radial gradient must have end radius");
- let stops = item["stops"]
- .as_vec()
- .expect("radial gradient must have stops")
- .chunks(2)
- .map(|chunk| {
- GradientStop {
- offset: chunk[0]
- .as_force_f32()
- .expect("gradient stop offset is not f32"),
- color: chunk[1]
- .as_colorf()
- .expect("gradient stop color is not color"),
- }
- })
- .collect::<Vec<_>>();
- let extend_mode = if item["repeat"].as_bool().unwrap_or(false) {
- ExtendMode::Repeat
- } else {
- ExtendMode::Clamp
- };
+ ExtendMode::Clamp
+ };
- dl.create_complex_radial_gradient(
- center,
- radius,
- start_radius,
- end_radius,
- stops,
- extend_mode,
- )
- }
+ dl.create_radial_gradient(center, radius, stops, extend_mode)
}
fn handle_rect(
&mut self,
dl: &mut DisplayListBuilder,
item: &Yaml,
info: &mut LayoutPrimitiveInfo,
) {
@@ -1434,21 +1402,16 @@ impl YamlFrameReader {
}
pub fn handle_push_shadow(
&mut self,
dl: &mut DisplayListBuilder,
yaml: &Yaml,
info: &mut LayoutPrimitiveInfo,
) {
- let rect = yaml["bounds"]
- .as_rect()
- .expect("Text shadows require bounds");
- info.rect = rect;
- info.clip_rect = rect;
let blur_radius = yaml["blur-radius"].as_f32().unwrap_or(0.0);
let offset = yaml["offset"].as_vector().unwrap_or(LayoutVector2D::zero());
let color = yaml["color"].as_colorf().unwrap_or(*BLACK_COLOR);
dl.push_shadow(
&info,
Shadow {
blur_radius,
--- a/gfx/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wrench/src/yaml_frame_writer.rs
@@ -268,16 +268,40 @@ fn native_font_handle_to_yaml(
_rsrc: &mut ResourceGenerator,
handle: &NativeFontHandle,
parent: &mut yaml_rust::yaml::Hash,
_: &mut Option<PathBuf>,
) {
str_node(parent, "font", &handle.pathname);
}
+fn radial_gradient_to_yaml(
+ table: &mut Table,
+ gradient: &webrender::api::RadialGradient,
+ stops_range: ItemRange<GradientStop>,
+ display_list: &BuiltDisplayList
+) {
+ point_node(table, "center", &gradient.center);
+ size_node(table, "radius", &gradient.radius);
+
+ let first_offset = gradient.start_offset;
+ let last_offset = gradient.end_offset;
+ let stops_delta = last_offset - first_offset;
+ assert!(first_offset <= last_offset);
+
+ let mut denormalized_stops = vec![];
+ for stop in display_list.get(stops_range) {
+ let denormalized_stop = (stop.offset * stops_delta) + first_offset;
+ denormalized_stops.push(Yaml::Real(denormalized_stop.to_string()));
+ denormalized_stops.push(Yaml::String(color_to_string(stop.color)));
+ }
+ yaml_node(table, "stops", Yaml::Array(denormalized_stops));
+ bool_node(table, "repeat", gradient.extend_mode == ExtendMode::Repeat);
+}
+
enum CachedFont {
Native(NativeFontHandle, Option<PathBuf>),
Raw(Option<Vec<u8>>, u32, Option<PathBuf>),
}
struct CachedFontInstance {
font_key: FontKey,
glyph_size: Au,
@@ -900,32 +924,23 @@ impl YamlFrameWriter {
let outset: Vec<f32> = vec![
details.outset.top,
details.outset.right,
details.outset.bottom,
details.outset.left,
];
yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
str_node(&mut v, "border-type", "radial-gradient");
- point_node(&mut v, "center", &details.gradient.center);
- size_node(&mut v, "radius", &details.gradient.radius);
- f32_node(&mut v, "start-radius", details.gradient.start_radius);
- f32_node(&mut v, "end-radius", details.gradient.end_radius);
- let mut stops = vec![];
- for stop in display_list.get(base.gradient_stops()) {
- stops.push(Yaml::Real(stop.offset.to_string()));
- stops.push(Yaml::String(color_to_string(stop.color)));
- }
- yaml_node(&mut v, "stops", Yaml::Array(stops));
- bool_node(
+ yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
+ radial_gradient_to_yaml(
&mut v,
- "repeat",
- details.gradient.extend_mode == ExtendMode::Repeat,
+ &details.gradient,
+ base.gradient_stops(),
+ display_list
);
- yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
}
}
}
BoxShadow(item) => {
str_node(&mut v, "type", "box-shadow");
rect_node(&mut v, "box-bounds", &item.box_bounds);
vector_node(&mut v, "offset", &item.offset);
color_node(&mut v, "color", item.color);
@@ -955,32 +970,23 @@ impl YamlFrameWriter {
bool_node(
&mut v,
"repeat",
item.gradient.extend_mode == ExtendMode::Repeat,
);
}
RadialGradient(item) => {
str_node(&mut v, "type", "radial-gradient");
- point_node(&mut v, "center", &item.gradient.center);
- size_node(&mut v, "center", &item.gradient.radius);
- f32_node(&mut v, "start-radius", item.gradient.start_radius);
- f32_node(&mut v, "end-radius", item.gradient.end_radius);
size_node(&mut v, "tile-size", &item.tile_size);
size_node(&mut v, "tile-spacing", &item.tile_spacing);
- let mut stops = vec![];
- for stop in display_list.get(base.gradient_stops()) {
- stops.push(Yaml::Real(stop.offset.to_string()));
- stops.push(Yaml::String(color_to_string(stop.color)));
- }
- yaml_node(&mut v, "stops", Yaml::Array(stops));
- bool_node(
+ radial_gradient_to_yaml(
&mut v,
- "repeat",
- item.gradient.extend_mode == ExtendMode::Repeat,
+ &item.gradient,
+ base.gradient_stops(),
+ display_list
);
}
Iframe(item) => {
str_node(&mut v, "type", "iframe");
u32_vec_node(&mut v, "id", &[item.pipeline_id.0, item.pipeline_id.1]);
}
PushStackingContext(item) => {
str_node(&mut v, "type", "stacking-context");