--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -35,17 +35,17 @@ smallvec = "0.6"
ws = { optional = true, version = "0.7.3" }
serde_json = { optional = true, version = "1.0" }
serde = { optional = true, version = "1.0", features = ["serde_derive"] }
image = { optional = true, version = "0.17" }
base64 = { optional = true, version = "0.3.0" }
ron = { optional = true, version = "0.1.7" }
[dev-dependencies]
-angle = {git = "https://github.com/servo/angle", branch = "servo"}
+mozangle = "0.1"
env_logger = "0.5"
rand = "0.3" # for the benchmarks
glutin = "0.12" # for the example apps
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.3", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -66,31 +66,41 @@ vec4[2] fetch_from_resource_cache_2(int
return vec4[2](
TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0))
);
}
#ifdef WR_VERTEX_SHADER
-#define VECS_PER_CLIP_SCROLL_NODE 5
+#define VECS_PER_CLIP_SCROLL_NODE 9
#define VECS_PER_LOCAL_CLIP_RECT 1
#define VECS_PER_RENDER_TASK 3
#define VECS_PER_PRIM_HEADER 2
#define VECS_PER_TEXT_RUN 3
#define VECS_PER_GRADIENT_STOP 2
uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes;
uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects;
uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
// Instanced attributes
in ivec4 aData0;
in ivec4 aData1;
+// Work around Angle bug that forgets to update sampler metadata,
+// by making the use of those samplers uniform across programs.
+// https://github.com/servo/webrender/wiki/Driver-issues#texturesize-in-vertex-shaders
+void markCacheTexturesUsed() {
+ vec2 size = vec2(textureSize(sCacheA8, 0)) + vec2(textureSize(sCacheRGBA8, 0));
+ if (size.x > 1000000.0) {
+ gl_Position = vec4(0.0);
+ }
+}
+
// get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
// TODO: convert back to a function once the driver issues are resolved, if ever.
// https://github.com/servo/webrender/pull/623
// https://github.com/servo/servo/issues/13953
#define get_fetch_uv(i, vpi) ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
vec4[8] fetch_from_resource_cache_8(int address) {
@@ -149,35 +159,42 @@ vec4 fetch_from_resource_cache_1_direct(
vec4 fetch_from_resource_cache_1(int address) {
ivec2 uv = get_resource_cache_uv(address);
return texelFetch(sResourceCache, uv, 0);
}
struct ClipScrollNode {
mat4 transform;
+ mat4 inv_transform;
bool is_axis_aligned;
};
ClipScrollNode fetch_clip_scroll_node(int index) {
ClipScrollNode node;
// Create a UV base coord for each 8 texels.
// This is required because trying to use an offset
// of more than 8 texels doesn't work on some versions
// of OSX.
ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE);
ivec2 uv0 = ivec2(uv.x + 0, uv.y);
+ ivec2 uv1 = ivec2(uv.x + 8, uv.y);
node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0));
node.transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(1, 0));
node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0));
node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0));
- vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
+ node.inv_transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
+ node.inv_transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0));
+ node.inv_transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0));
+ node.inv_transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(7, 0));
+
+ vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv1, 0, ivec2(0, 0));
node.is_axis_aligned = misc.x == 0.0;
return node;
}
RectWithSize fetch_clip_chain_rect(int index) {
ivec2 uv = get_fetch_uv(index, VECS_PER_LOCAL_CLIP_RECT);
vec4 rect = TEXEL_FETCH(sLocalClipRects, uv, 0, ivec2(0, 0));
@@ -353,16 +370,18 @@ PrimitiveInstance fetch_prim_instance()
pi.clip_task_index = aData0.z;
pi.clip_chain_rect_index = aData0.w / 65536;
pi.scroll_node_id = aData0.w % 65536;
pi.z = aData1.x;
pi.user_data0 = aData1.y;
pi.user_data1 = aData1.z;
pi.user_data2 = aData1.w;
+ markCacheTexturesUsed();
+
return pi;
}
struct CompositeInstance {
int render_task_index;
int src_task_index;
int backdrop_task_index;
int user_data0;
@@ -380,16 +399,18 @@ CompositeInstance fetch_composite_instan
ci.backdrop_task_index = aData0.z;
ci.z = float(aData0.w);
ci.user_data0 = aData1.x;
ci.user_data1 = aData1.y;
ci.user_data2 = aData1.z;
ci.user_data3 = aData1.w;
+ markCacheTexturesUsed();
+
return ci;
}
struct Primitive {
ClipScrollNode scroll_node;
ClipArea clip_area;
PictureTask task;
RectWithSize local_rect;
@@ -471,19 +492,18 @@ vec4 untransform(vec2 ref, vec3 n, vec3
// Given a CSS space position, transform it back into the ClipScrollNode space.
vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
// get a point on the scroll node plane
vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0);
vec3 a = ah.xyz / ah.w;
// get the normal to the scroll node plane
- mat4 inv_transform = inverse(node.transform);
- vec3 n = transpose(mat3(inv_transform)) * vec3(0.0, 0.0, 1.0);
- return untransform(pos, n, a, inv_transform);
+ vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0);
+ return untransform(pos, n, a, node.inv_transform);
}
// Compute a snapping offset in world space (adjusted to pixel ratio),
// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
ClipScrollNode scroll_node,
RectWithSize snap_rect) {
// Ensure that the snap rect is at *least* one device pixel in size.
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -240,21 +240,16 @@ impl ClipSources {
tile: None,
},
gpu_cache,
);
}
}
}
- /// Whether or not this ClipSources has any clips (does any clipping).
- pub fn has_clips(&self) -> bool {
- !self.clips.is_empty()
- }
-
pub fn get_screen_bounds(
&self,
transform: &LayerToWorldFastTransform,
device_pixel_scale: DevicePixelScale,
) -> (DeviceIntRect, Option<DeviceIntRect>) {
// If this translation isn't axis aligned or has a perspective component, don't try to
// calculate the inner rectangle. The rectangle that we produce would include potentially
// clipped screen area.
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -281,23 +281,32 @@ impl ClipScrollNode {
}
pub fn push_gpu_node_data(&mut self, node_data: &mut Vec<ClipScrollNodeData>) {
if !self.invertible {
node_data.push(ClipScrollNodeData::invalid());
return;
}
+ let inv_transform = match self.world_content_transform.inverse() {
+ Some(inverted) => inverted.to_transform(),
+ None => {
+ node_data.push(ClipScrollNodeData::invalid());
+ return;
+ }
+ };
+
let transform_kind = if self.world_content_transform.preserves_2d_axis_alignment() {
TransformedRectKind::AxisAligned
} else {
TransformedRectKind::Complex
};
let data = ClipScrollNodeData {
transform: self.world_content_transform.into(),
+ inv_transform,
transform_kind: transform_kind as u32 as f32,
padding: [0.0; 3],
};
// Write the data that will be made available to the GPU for this node.
node_data.push(data);
}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -211,24 +211,23 @@ impl<'a> DisplayListFlattener<'a> {
old_builder: FrameBuilder,
scene: &Scene,
clip_scroll_tree: &mut ClipScrollTree,
font_instances: FontInstanceMap,
tiled_image_map: TiledImageMap,
view: &DocumentView,
output_pipelines: &FastHashSet<PipelineId>,
frame_builder_config: &FrameBuilderConfig,
- pipeline_epochs: &mut FastHashMap<PipelineId, Epoch>,
+ new_scene: &mut Scene,
) -> FrameBuilder {
// We checked that the root pipeline is available on the render backend.
let root_pipeline_id = scene.root_pipeline_id.unwrap();
let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
let root_epoch = scene.pipeline_epochs[&root_pipeline_id];
- pipeline_epochs.insert(root_pipeline_id, root_epoch);
let background_color = root_pipeline
.background_color
.and_then(|color| if color.a > 0.0 { Some(color) } else { None });
let mut flattener = DisplayListFlattener {
scene,
clip_scroll_tree,
@@ -256,17 +255,21 @@ impl<'a> DisplayListFlattener<'a> {
root_pipeline_id,
&root_pipeline.viewport_size,
&root_pipeline.content_size,
);
flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
debug_assert!(flattener.picture_stack.is_empty());
- pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
+
+ new_scene.root_pipeline_id = Some(root_pipeline_id);
+ new_scene.pipeline_epochs.insert(root_pipeline_id, root_epoch);
+ new_scene.pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
+ new_scene.pipelines = scene.pipelines.clone();
FrameBuilder::with_display_list_flattener(
view.inner_rect,
background_color,
view.window_size,
flattener
)
}
@@ -1159,17 +1162,22 @@ impl<'a> DisplayListFlattener<'a> {
region.rect,
region.radii,
region.mode,
));
}
let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
- let clip_sources = self.clip_store.insert(ClipSources::new(clip_sources));
+ let clip_sources = if clip_sources.is_empty() {
+ None
+ } else {
+ Some(self.clip_store.insert(ClipSources::new(clip_sources)))
+ };
+
let prim_index = self.prim_store.add_primitive(
&info.rect,
&info.local_clip.clip_rect(),
info.is_backface_visible && stacking_context.is_backface_visible,
clip_sources,
info.tag,
container,
);
@@ -1266,28 +1274,25 @@ impl<'a> DisplayListFlattener<'a> {
let pic = PicturePrimitive::new_image(
None,
false,
pipeline_id,
current_reference_frame_index,
None,
);
- // No clip sources needed for the main framebuffer.
- let clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
-
// Add root picture primitive. The provided layer rect
// is zero, because we don't yet know the size of the
// picture. Instead, this is calculated recursively
// when we cull primitives.
let prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
true,
- clip_sources,
+ None,
None,
PrimitiveContainer::Picture(pic),
);
self.picture_stack.push(prim_index);
} else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
// If we have a mix-blend-mode, and we aren't the primary framebuffer,
// the stacking context needs to be isolated to blend correctly as per
@@ -1346,23 +1351,21 @@ impl<'a> DisplayListFlattener<'a> {
let container = PicturePrimitive::new_image(
None,
false,
pipeline_id,
current_reference_frame_index,
None,
);
- let clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
-
let prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
- clip_sources,
+ None,
None,
PrimitiveContainer::Picture(container),
);
let parent_pic_prim_index = *self.picture_stack.last().unwrap();
let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
pic.add_primitive(
@@ -1397,23 +1400,22 @@ impl<'a> DisplayListFlattener<'a> {
for filter in composite_ops.filters.iter().rev() {
let src_prim = PicturePrimitive::new_image(
Some(PictureCompositeMode::Filter(*filter)),
false,
pipeline_id,
current_reference_frame_index,
None,
);
- let src_clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
let src_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
- src_clip_sources,
+ None,
None,
PrimitiveContainer::Picture(src_prim),
);
let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
parent_pic_prim_index = src_prim_index;
let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
pic.add_primitive(
@@ -1428,23 +1430,22 @@ impl<'a> DisplayListFlattener<'a> {
if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
let src_prim = PicturePrimitive::new_image(
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
false,
pipeline_id,
current_reference_frame_index,
None,
);
- let src_clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
let src_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
- src_clip_sources,
+ None,
None,
PrimitiveContainer::Picture(src_prim),
);
let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
parent_pic_prim_index = src_prim_index;
let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
pic.add_primitive(
@@ -1482,22 +1483,21 @@ impl<'a> DisplayListFlattener<'a> {
let sc_prim = PicturePrimitive::new_image(
composite_mode,
participating_in_3d_context,
pipeline_id,
current_reference_frame_index,
frame_output_pipeline_id,
);
- let sc_clip_sources = self.clip_store.insert(ClipSources::new(Vec::new()));
let sc_prim_index = self.prim_store.add_primitive(
&LayerRect::zero(),
&max_clip,
is_backface_visible,
- sc_clip_sources,
+ None,
None,
PrimitiveContainer::Picture(sc_prim),
);
let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
let sc_pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
sc_pic.add_primitive(
sc_prim_index,
@@ -1641,18 +1641,16 @@ impl<'a> DisplayListFlattener<'a> {
pub fn add_clip_node(
&mut self,
new_node_id: ClipId,
parent_id: ClipId,
clip_region: ClipRegion,
) -> ClipScrollNodeIndex {
let clip_rect = clip_region.main;
let clip_sources = ClipSources::from(clip_region);
-
- debug_assert!(clip_sources.has_clips());
let handle = self.clip_store.insert(clip_sources);
let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
let clip_chain_index = self.clip_scroll_tree.add_clip_node(
node_index,
self.id_to_index_mapper.get_node_index(parent_id),
handle,
clip_rect,
@@ -2616,38 +2614,34 @@ impl<'a> DisplayListFlattener<'a> {
info,
Vec::new(),
PrimitiveContainer::Brush(prim),
);
}
}
pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
- // TODO: mutably pass the scene and update its own pipeline epoch map instead of
- // creating a new one here.
- let mut pipeline_epoch_map = FastHashMap::default();
+
let mut clip_scroll_tree = ClipScrollTree::new();
+ let mut new_scene = Scene::new();
let frame_builder = DisplayListFlattener::create_frame_builder(
FrameBuilder::empty(), // WIP, we're not really recycling anything here, clean this up.
&request.scene,
&mut clip_scroll_tree,
request.font_instances,
request.tiled_image_map,
&request.view,
&request.output_pipelines,
config,
- &mut pipeline_epoch_map
+ &mut new_scene
);
- let mut scene = request.scene;
- scene.pipeline_epochs = pipeline_epoch_map;
-
BuiltScene {
- scene,
+ scene: new_scene,
frame_builder,
clip_scroll_tree,
removed_pipelines: request.removed_pipelines,
}
}
trait PrimitiveInfoTiler {
fn decompose(
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -1,13 +1,13 @@
/* 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::{DevicePoint, LayerToWorldTransform, PremultipliedColorF};
+use api::{DevicePoint, LayerToWorldTransform, PremultipliedColorF, WorldToLayerTransform};
use gpu_cache::{GpuCacheAddress, GpuDataRequest};
use prim_store::EdgeAaSegmentMask;
use render_task::RenderTaskAddress;
// Contains type that must exactly match the same structures declared in GLSL.
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
@@ -212,24 +212,26 @@ pub enum BrushImageKind {
pub struct ClipScrollNodeIndex(pub u32);
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[repr(C)]
pub struct ClipScrollNodeData {
pub transform: LayerToWorldTransform,
+ pub inv_transform: WorldToLayerTransform,
pub transform_kind: f32,
pub padding: [f32; 3],
}
impl ClipScrollNodeData {
pub fn invalid() -> Self {
ClipScrollNodeData {
transform: LayerToWorldTransform::identity(),
+ inv_transform: WorldToLayerTransform::identity(),
transform_kind: 0.0,
padding: [0.0; 3],
}
}
}
#[derive(Copy, Debug, Clone, PartialEq)]
#[repr(C)]
@@ -268,9 +270,9 @@ impl ImageSource {
request.push([
self.texture_layer,
self.user_data[0],
self.user_data[1],
self.user_data[2],
]);
request.push(self.color);
}
-}
\ No newline at end of file
+}
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -1,28 +1,44 @@
/* 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::{BoxShadowClipMode, ColorF, DeviceIntPoint, DeviceIntRect, FilterOp, LayerPoint};
-use api::{LayerRect, LayerToWorldScale, LayerVector2D, MixBlendMode, PipelineId};
+use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize};
+use api::{LayerPoint, LayerRect, LayerToWorldScale, LayerVector2D};
+use api::{BoxShadowClipMode, ColorF, FilterOp, MixBlendMode, PipelineId};
use api::{PremultipliedColorF, Shadow};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
use clip_scroll_tree::ClipScrollNodeIndex;
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState};
use gpu_cache::{GpuCacheHandle, GpuDataRequest};
use gpu_types::{BrushImageKind, PictureType};
use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use prim_store::ScrollNodeAndClipChain;
use render_task::{ClearMode, RenderTask, RenderTaskCacheKey};
use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
+
+// TODO(gw): Rounding the content rect here to device pixels is not
+// technically correct. Ideally we should ceil() here, and ensure that
+// the extra part pixel in the case of fractional sizes is correctly
+// handled. For now, just use rounding which passes the existing
+// Gecko tests.
+// Note: zero-square tasks are prohibited in WR task tree, so
+// we ensure each dimension to be at least the length of 1 after rounding.
+fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
+ DeviceIntSize::new(
+ 1.max(size.width.round() as i32),
+ 1.max(size.height.round() as i32),
+ )
+}
+
/*
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
picture into its parent.
* A configuration describing how to draw the primitives on
this picture (e.g. in screen space or local space).
@@ -469,23 +485,17 @@ impl PicturePrimitive {
}
}
}
PictureKind::TextShadow { blur_radius, color, content_rect, .. } => {
// 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.
-
- // TODO(gw): Rounding the content rect here to device pixels is not
- // technically correct. Ideally we should ceil() here, and ensure that
- // the extra part pixel in the case of fractional sizes is correctly
- // handled. For now, just use rounding which passes the existing
- // Gecko tests.
- let cache_size = (content_rect.size * content_scale).round().to_i32();
+ 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 picture_task = RenderTask::new_picture(
@@ -515,17 +525,17 @@ impl PicturePrimitive {
self.surface = Some(PictureSurface::RenderTask(render_task_id));
}
PictureKind::BoxShadow { blur_radius, clip_mode, color, content_rect, cache_key, .. } => {
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
- let cache_size = (content_rect.size * content_scale).round().to_i32();
+ let cache_size = to_cache_size(content_rect.size * content_scale);
// Request the texture cache item for this box-shadow key. If it
// doesn't exist in the cache, the closure is invoked to build
// a render task chain to draw the cacheable result.
let cache_item = frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size: cache_size,
kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -163,17 +163,17 @@ pub struct ScreenRect {
pub clipped: DeviceIntRect,
pub unclipped: DeviceIntRect,
}
// TODO(gw): Pack the fields here better!
#[derive(Debug)]
pub struct PrimitiveMetadata {
pub opacity: PrimitiveOpacity,
- pub clip_sources: ClipSourcesHandle,
+ pub clip_sources: Option<ClipSourcesHandle>,
pub prim_kind: PrimitiveKind,
pub cpu_prim_index: SpecificPrimitiveIndex,
pub gpu_location: GpuCacheHandle,
pub clip_task_id: Option<RenderTaskId>,
// TODO(gw): In the future, we should just pull these
// directly from the DL item, instead of
// storing them here.
@@ -959,17 +959,17 @@ impl PrimitiveStore {
}
}
pub fn add_primitive(
&mut self,
local_rect: &LayerRect,
local_clip_rect: &LayerRect,
is_backface_visible: bool,
- clip_sources: ClipSourcesHandle,
+ clip_sources: Option<ClipSourcesHandle>,
tag: Option<ItemTag>,
container: PrimitiveContainer,
) -> PrimitiveIndex {
let prim_index = self.cpu_metadata.len();
let base_metadata = PrimitiveMetadata {
clip_sources,
gpu_location: GpuCacheHandle::new(),
@@ -1565,47 +1565,45 @@ impl PrimitiveStore {
let mut combined_outer_rect =
prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect);
let clip_chain = prim_run_context.clip_chain.nodes.clone();
let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
let transform = &prim_run_context.scroll_node.world_content_transform;
let extra_clip = {
let metadata = &self.cpu_metadata[prim_index.0];
- let prim_clips = frame_state.clip_store.get_mut(&metadata.clip_sources);
- if prim_clips.has_clips() {
+ metadata.clip_sources.as_ref().map(|ref clip_sources| {
+ let prim_clips = frame_state.clip_store.get_mut(clip_sources);
prim_clips.update(
frame_state.gpu_cache,
frame_state.resource_cache,
);
let (screen_inner_rect, screen_outer_rect) =
prim_clips.get_screen_bounds(transform, frame_context.device_pixel_scale);
if let Some(outer) = screen_outer_rect {
combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer));
}
- Some(Arc::new(ClipChainNode {
+ Arc::new(ClipChainNode {
work_item: ClipWorkItem {
scroll_node_data_index: prim_run_context.scroll_node.node_data_index,
- clip_sources: metadata.clip_sources.weak(),
+ clip_sources: clip_sources.weak(),
coordinate_system_id: prim_coordinate_system_id,
},
// The local_clip_rect a property of ClipChain nodes that are ClipScrollNodes.
// It's used to calculate a local clipping rectangle before we reach this
// point, so we can set it to zero here. It should be unused from this point
// on.
local_clip_rect: LayerRect::zero(),
screen_inner_rect,
screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect),
prev: None,
- }))
- } else {
- None
- }
+ })
+ })
};
// If everything is clipped out, then we don't need to render this primitive.
let combined_outer_rect = match combined_outer_rect {
Some(rect) if !rect.is_empty() => rect,
_ => {
self.cpu_metadata[prim_index.0].screen_rect = None;
return false;
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -167,23 +167,59 @@ impl Document {
dynamic_properties: SceneProperties::new(),
}
}
fn can_render(&self) -> bool { self.frame_builder.is_some() }
// TODO: We will probably get rid of this soon and always forward to the scene building thread.
fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
- let frame_builder = self.create_frame_builder(resource_cache);
+
+ if self.view.window_size.width == 0 || self.view.window_size.height == 0 {
+ error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
+ }
+
+ let old_builder = self.frame_builder.take().unwrap_or_else(FrameBuilder::empty);
+ let root_pipeline_id = match self.pending.scene.root_pipeline_id {
+ Some(root_pipeline_id) => root_pipeline_id,
+ None => return,
+ };
+
+ if !self.pending.scene.pipelines.contains_key(&root_pipeline_id) {
+ return;
+ }
+
+ // The DisplayListFlattener will re-create the up-to-date current scene's pipeline epoch
+ // map and clip scroll tree from the information in the pending scene.
+ self.current.scene.pipeline_epochs.clear();
+ let old_scrolling_states = self.clip_scroll_tree.drain();
+
+ let frame_builder = DisplayListFlattener::create_frame_builder(
+ old_builder,
+ &self.pending.scene,
+ &mut self.clip_scroll_tree,
+ resource_cache.get_font_instances(),
+ resource_cache.get_tiled_image_map(),
+ &self.view,
+ &self.output_pipelines,
+ &self.frame_builder_config,
+ &mut self.current.scene,
+ );
+
+ self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
+
if !self.current.removed_pipelines.is_empty() {
warn!("Built the scene several times without rendering it.");
}
+
self.current.removed_pipelines.extend(self.pending.removed_pipelines.drain(..));
self.frame_builder = Some(frame_builder);
- self.current.scene = self.pending.scene.clone();
+
+ // Advance to the next frame.
+ self.frame_id.0 += 1;
}
fn forward_transaction_to_scene_builder(
&mut self,
transaction_msg: TransactionMsg,
document_ops: &DocumentOps,
document_id: DocumentId,
resource_cache: &ResourceCache,
@@ -307,58 +343,16 @@ impl Document {
let old_scrolling_states = self.clip_scroll_tree.drain();
self.clip_scroll_tree = built_scene.clip_scroll_tree;
self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
// Advance to the next frame.
self.frame_id.0 += 1;
}
-
- // When changing this, please make the same modification to build_scene,
- // which will soon replace this method completely.
- pub fn create_frame_builder(&mut self, resource_cache: &mut ResourceCache) -> FrameBuilder {
- if self.view.window_size.width == 0 || self.view.window_size.height == 0 {
- error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
- }
-
- let old_builder = self.frame_builder.take().unwrap_or_else(FrameBuilder::empty);
- let root_pipeline_id = match self.pending.scene.root_pipeline_id {
- Some(root_pipeline_id) => root_pipeline_id,
- None => return old_builder,
- };
-
- if !self.pending.scene.pipelines.contains_key(&root_pipeline_id) {
- return old_builder;
- }
-
- // The DisplayListFlattener will re-create the up-to-date current scene's pipeline epoch
- // map and clip scroll tree from the information in the pending scene.
- self.current.scene.pipeline_epochs.clear();
- let old_scrolling_states = self.clip_scroll_tree.drain();
-
- let frame_builder = DisplayListFlattener::create_frame_builder(
- old_builder,
- &self.pending.scene,
- &mut self.clip_scroll_tree,
- resource_cache.get_font_instances(),
- resource_cache.get_tiled_image_map(),
- &self.view,
- &self.output_pipelines,
- &self.frame_builder_config,
- &mut self.current.scene.pipeline_epochs,
- );
-
- self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
-
- // Advance to the next frame.
- self.frame_id.0 += 1;
-
- frame_builder
- }
}
struct DocumentOps {
scroll: bool,
build: bool,
render: bool,
composite: bool,
}
@@ -1144,20 +1138,25 @@ impl RenderBackend {
#[cfg(feature = "capture")]
// Note: the mutable `self` is only needed here for resolving blob images
fn save_capture(
&mut self,
root: PathBuf,
bits: CaptureBits,
profile_counters: &mut BackendProfileCounters,
) -> DebugOutput {
+ use std::fs;
use capture::CaptureConfig;
debug!("capture: saving {:?}", root);
- let (resources, deferred) = self.resource_cache.save_capture(&root);
+ if !root.is_dir() {
+ if let Err(e) = fs::create_dir_all(&root) {
+ panic!("Unable to create capture dir: {:?}", e);
+ }
+ }
let config = CaptureConfig::new(root, bits);
for (&id, doc) in &mut self.documents {
debug!("\tdocument {:?}", id);
if config.bits.contains(CaptureBits::SCENE) {
let file_name = format!("scene-{}-{}", (id.0).0, id.1);
config.serialize(&doc.current.scene, file_name);
}
@@ -1170,16 +1169,19 @@ impl RenderBackend {
//TODO: write down full `RenderedDocument`?
// it has `pipeline_epoch_map` and `layers_bouncing_back`,
// which may capture necessary details for some cases.
let file_name = format!("frame-{}-{}", (id.0).0, id.1);
config.serialize(&rendered_document.frame, file_name);
}
}
+ debug!("\tresource cache");
+ let (resources, deferred) = self.resource_cache.save_capture(&config.root);
+
info!("\tbackend");
let backend = PlainRenderBackend {
default_device_pixel_ratio: self.default_device_pixel_ratio,
enable_render_on_scroll: self.enable_render_on_scroll,
frame_config: self.frame_config.clone(),
documents: self.documents
.iter()
.map(|(id, doc)| (*id, doc.view.clone()))
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -1055,19 +1055,16 @@ impl ResourceCache {
) -> (PlainResources, Vec<ExternalCaptureImage>) {
#[cfg(feature = "png")]
use device::ReadPixelsFormat;
use std::fs;
use std::io::Write;
info!("saving resource cache");
let res = &self.resources;
- if !root.is_dir() {
- fs::create_dir_all(root).unwrap()
- }
let path_fonts = root.join("fonts");
if !path_fonts.is_dir() {
fs::create_dir(&path_fonts).unwrap();
}
let path_images = root.join("images");
if !path_images.is_dir() {
fs::create_dir(&path_images).unwrap();
}
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -141,16 +141,17 @@ impl Scene {
self.pipeline_epochs.insert(pipeline_id, epoch);
}
pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
if self.root_pipeline_id == Some(pipeline_id) {
self.root_pipeline_id = None;
}
self.pipelines.remove(&pipeline_id);
+ self.pipeline_epochs.remove(&pipeline_id);
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
self.pipeline_epochs.insert(pipeline_id, epoch);
}
}
/// An arbitrary number which we assume opacity is invisible below.
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -396,17 +396,17 @@ impl<Src, Dst> FastTransform<Src, Dst> {
#[inline(always)]
pub fn pre_mul<NewSrc>(
&self,
other: &FastTransform<NewSrc, Src>
) -> FastTransform<NewSrc, Dst> {
match (self, other) {
(&FastTransform::Offset(ref offset), &FastTransform::Offset(ref other_offset)) => {
let offset = TypedVector2D::from_untyped(&offset.to_untyped());
- FastTransform::Offset((offset + *other_offset))
+ FastTransform::Offset(offset + *other_offset)
}
_ => {
let new_transform = self.to_transform().pre_mul(&other.to_transform());
FastTransform::with_transform(new_transform)
}
}
}
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -1,16 +1,16 @@
/* 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/. */
-extern crate angle;
+extern crate mozangle;
extern crate webrender;
-use angle::hl::{BuiltInResources, Output, ShaderSpec, ShaderValidator};
+use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator};
// from glslang
const FRAGMENT_SHADER: u32 = 0x8B30;
const VERTEX_SHADER: u32 = 0x8B31;
struct Shader {
name: &'static str,
features: &'static [&'static str],
@@ -108,17 +108,17 @@ const SHADERS: &[Shader] = &[
features: &[],
},
];
const VERSION_STRING: &str = "#version 300 es\n";
#[test]
fn validate_shaders() {
- angle::hl::initialize().unwrap();
+ mozangle::shaders::initialize().unwrap();
let resources = BuiltInResources::default();
let vs_validator =
ShaderValidator::new(VERTEX_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();
let fs_validator =
ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap();
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-22b831c02479eea31821f49a0fac7dd699083557
+0da6c839b3a0e165f1115fb9fe286be7540c24ed