--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -38,24 +38,24 @@ impl Example for App {
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
// Create a 200x200 stacking context with an animated transform property.
let bounds = (0, 0).to(200, 200);
let filters = vec![
- FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key), self.opacity),
+ FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
];
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
- Some(PropertyBinding::Binding(self.property_key)),
+ Some(PropertyBinding::Binding(self.property_key, LayoutTransform::identity())),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
filters,
GlyphRasterSpace::Screen,
);
let complex_clip = ComplexClipRegion {
--- a/gfx/webrender/examples/iframe.rs
+++ b/gfx/webrender/examples/iframe.rs
@@ -60,17 +60,17 @@ impl Example for App {
true,
);
api.send_transaction(document_id, txn);
// And this is for the root pipeline
builder.push_stacking_context(
&info,
None,
- Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
+ Some(PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity())),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
// red rect under the iframe: if this is visible, things have gone wrong
builder.push_rect(&info, ColorF::new(1.0, 0.0, 0.0, 1.0));
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -15,18 +15,16 @@
#define RASTER_SCREEN 1
uniform sampler2DArray sCacheA8;
uniform sampler2DArray sCacheRGBA8;
// An A8 target for standalone tasks that is available to all passes.
uniform sampler2DArray sSharedCacheA8;
-uniform sampler2D sGradients;
-
vec2 clamp_rect(vec2 pt, RectWithSize rect) {
return clamp(pt, rect.p0, rect.p0 + rect.size);
}
float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
vec2 dir_to_p0 = p0 - p;
return dot(normalize(perp_dir), dir_to_p0);
}
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -16,17 +16,18 @@ use gpu_types::{BrushFlags, BrushInstanc
use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, CompositePrimitiveInstance};
use gpu_types::{PrimitiveInstance, RasterizationSpace, SimplePrimitiveInstance, ZBufferId};
use gpu_types::ZBufferIdGenerator;
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, CachedGradient, DeferredResolve};
use prim_store::{EdgeAaSegmentMask, ImageSource, PictureIndex, PrimitiveIndex, PrimitiveKind};
-use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore};
+use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore, VisibleGradientTile};
+use prim_store::CachedGradientIndex;
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::{BLOCKS_PER_UV_RECT, ShaderColorMode};
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use scene::FilterOpHelpers;
use std::{usize, f32, i32};
use tiling::{RenderTargetContext};
use util::{MatrixHelpers, TransformedRectKind};
@@ -610,16 +611,18 @@ impl AlphaBatchBuilder {
// If the primitive is internally decomposed into multiple sub-primitives we may not
// use some of the per-primitive data typically stored in PrimitiveMetadata and get
// it from each sub-primitive instead.
let is_multiple_primitives = match prim_metadata.prim_kind {
PrimitiveKind::Brush => {
let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0];
match brush.kind {
BrushKind::Image { ref visible_tiles, .. } => !visible_tiles.is_empty(),
+ BrushKind::LinearGradient { ref visible_tiles, .. } => !visible_tiles.is_empty(),
+ BrushKind::RadialGradient { ref visible_tiles, .. } => !visible_tiles.is_empty(),
_ => false,
}
}
_ => false,
};
let prim_cache_address = if is_multiple_primitives {
GpuCacheAddress::invalid()
@@ -1013,16 +1016,50 @@ impl AlphaBatchBuilder {
task_address,
z,
user_data,
tile.edge_flags
);
}
}
}
+ BrushKind::LinearGradient { gradient_index, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
+ add_gradient_tiles(
+ visible_tiles,
+ gradient_index,
+ BrushBatchKind::LinearGradient,
+ specified_blend_mode,
+ &task_relative_bounding_rect,
+ clip_chain_rect_index,
+ scroll_id,
+ task_address,
+ clip_task_address,
+ z,
+ ctx,
+ gpu_cache,
+ &mut self.batch_list,
+ );
+ }
+ BrushKind::RadialGradient { gradient_index, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
+ add_gradient_tiles(
+ visible_tiles,
+ gradient_index,
+ BrushBatchKind::RadialGradient,
+ specified_blend_mode,
+ &task_relative_bounding_rect,
+ clip_chain_rect_index,
+ scroll_id,
+ task_address,
+ clip_task_address,
+ z,
+ ctx,
+ gpu_cache,
+ &mut self.batch_list,
+ );
+ }
_ => {
if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
ctx.resource_cache,
gpu_cache,
deferred_resolves,
ctx.cached_gradients,
) {
self.add_brush_to_batch(
@@ -1343,16 +1380,67 @@ impl AlphaBatchBuilder {
};
let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(base_instance));
}
}
}
}
+fn add_gradient_tiles(
+ visible_tiles: &[VisibleGradientTile],
+ gradient_index: CachedGradientIndex,
+ kind: BrushBatchKind,
+ blend_mode: BlendMode,
+ task_relative_bounding_rect: &DeviceIntRect,
+ clip_chain_rect_index: ClipChainRectIndex,
+ scroll_id: ClipScrollNodeIndex,
+ task_address: RenderTaskAddress,
+ clip_task_address: RenderTaskAddress,
+ z: ZBufferId,
+ ctx: &RenderTargetContext,
+ gpu_cache: &GpuCache,
+ batch_list: &mut BatchList,
+) {
+ batch_list.add_bounding_rect(task_relative_bounding_rect);
+ let batch = batch_list.get_suitable_batch(
+ BatchKey {
+ blend_mode: blend_mode,
+ kind: BatchKind::Brush(kind),
+ textures: BatchTextures::no_texture(),
+ },
+ task_relative_bounding_rect
+ );
+
+ let stops_handle = &ctx.cached_gradients[gradient_index.0].handle;
+ let user_data = [stops_handle.as_int(gpu_cache), 0, 0];
+
+ let base_instance = BrushInstance {
+ picture_address: task_address,
+ prim_address: GpuCacheAddress::invalid(),
+ clip_chain_rect_index,
+ scroll_id,
+ clip_task_address,
+ z,
+ segment_index: 0,
+ edge_flags: EdgeAaSegmentMask::all(),
+ brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION,
+ user_data,
+ };
+
+ for tile in visible_tiles {
+ batch.push(PrimitiveInstance::from(
+ BrushInstance {
+ prim_address: gpu_cache.get_address(&tile.handle),
+ ..base_instance
+ }
+ ));
+ }
+}
+
fn get_image_tile_params(
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut Vec<DeferredResolve>,
request: ImageRequest,
) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> {
let cache_item = resolve_image(
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -1795,28 +1795,38 @@ impl<'a> DisplayListFlattener<'a> {
segment.size,
LayoutSize::zero(),
);
}
}
}
}
- fn add_gradient_impl(
+ pub fn add_gradient(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayoutPrimitiveInfo,
start_point: LayoutPoint,
end_point: LayoutPoint,
stops: ItemRange<GradientStop>,
stops_count: usize,
extend_mode: ExtendMode,
- gradient_index: CachedGradientIndex,
stretch_size: LayoutSize,
+ mut tile_spacing: LayoutSize,
) {
+ let gradient_index = CachedGradientIndex(self.cached_gradients.len());
+ self.cached_gradients.push(CachedGradient::new());
+
+ let mut prim_rect = info.rect;
+ simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
+ let info = LayoutPrimitiveInfo {
+ rect: prim_rect,
+ .. *info
+ };
+
// Try to ensure that if the gradient is specified in reverse, then so long as the stops
// are also supplied in reverse that the rendered result will be equivalent. To do this,
// a reference orientation for the gradient line must be chosen, somewhat arbitrarily, so
// just designate the reference orientation as start < end. Aligned gradient rendering
// manages to produce the same result regardless of orientation, so don't worry about
// reversing in that case.
let reverse_stops = start_point.x > end_point.x ||
(start_point.x == end_point.x && start_point.y > end_point.y);
@@ -1835,119 +1845,25 @@ impl<'a> DisplayListFlattener<'a> {
stops_range: stops,
stops_count,
extend_mode,
reverse_stops,
start_point: sp,
end_point: ep,
gradient_index,
stretch_size,
+ tile_spacing,
+ visible_tiles: Vec::new(),
},
None,
);
let prim = PrimitiveContainer::Brush(prim);
- self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
- }
-
- pub fn add_gradient(
- &mut self,
- clip_and_scroll: ScrollNodeAndClipChain,
- info: &LayoutPrimitiveInfo,
- start_point: LayoutPoint,
- end_point: LayoutPoint,
- stops: ItemRange<GradientStop>,
- stops_count: usize,
- extend_mode: ExtendMode,
- stretch_size: LayoutSize,
- mut tile_spacing: LayoutSize,
- ) {
- let gradient_index = CachedGradientIndex(self.cached_gradients.len());
- self.cached_gradients.push(CachedGradient::new());
-
- let mut prim_rect = info.rect;
- simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
- let info = LayoutPrimitiveInfo {
- rect: prim_rect,
- .. *info
- };
-
- if tile_spacing != LayoutSize::zero() {
- let prim_infos = info.decompose(
- stretch_size,
- tile_spacing,
- 64 * 64,
- );
-
- if !prim_infos.is_empty() {
- for prim_info in prim_infos {
- self.add_gradient_impl(
- clip_and_scroll,
- &prim_info,
- start_point,
- end_point,
- stops,
- stops_count,
- extend_mode,
- gradient_index,
- prim_info.rect.size,
- );
- }
-
- return;
- }
- }
-
- self.add_gradient_impl(
- clip_and_scroll,
- &info,
- start_point,
- end_point,
- stops,
- stops_count,
- extend_mode,
- gradient_index,
- stretch_size,
- );
- }
-
- fn add_radial_gradient_impl(
- &mut self,
- clip_and_scroll: ScrollNodeAndClipChain,
- info: &LayoutPrimitiveInfo,
- center: LayoutPoint,
- start_radius: f32,
- end_radius: f32,
- ratio_xy: f32,
- stops: ItemRange<GradientStop>,
- extend_mode: ExtendMode,
- gradient_index: CachedGradientIndex,
- stretch_size: LayoutSize,
- ) {
- let prim = BrushPrimitive::new(
- BrushKind::RadialGradient {
- stops_range: stops,
- extend_mode,
- center,
- start_radius,
- end_radius,
- ratio_xy,
- gradient_index,
- stretch_size,
- },
- None,
- );
-
- self.add_primitive(
- clip_and_scroll,
- info,
- Vec::new(),
- PrimitiveContainer::Brush(prim),
- );
+ self.add_primitive(clip_and_scroll, &info, Vec::new(), prim);
}
pub fn add_radial_gradient(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayoutPrimitiveInfo,
center: LayoutPoint,
start_radius: f32,
@@ -1963,54 +1879,37 @@ impl<'a> DisplayListFlattener<'a> {
let mut prim_rect = info.rect;
simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
let info = LayoutPrimitiveInfo {
rect: prim_rect,
.. *info
};
- if tile_spacing != LayoutSize::zero() {
- let prim_infos = info.decompose(
+ let prim = BrushPrimitive::new(
+ BrushKind::RadialGradient {
+ stops_range: stops,
+ extend_mode,
+ center,
+ start_radius,
+ end_radius,
+ ratio_xy,
+ gradient_index,
stretch_size,
tile_spacing,
- 64 * 64,
- );
+ visible_tiles: Vec::new(),
+ },
+ None,
+ );
- if !prim_infos.is_empty() {
- for prim_info in prim_infos {
- self.add_radial_gradient_impl(
- clip_and_scroll,
- &prim_info,
- center,
- start_radius,
- end_radius,
- ratio_xy,
- stops,
- extend_mode,
- gradient_index,
- stretch_size,
- );
- }
-
- return;
- }
- }
-
- self.add_radial_gradient_impl(
+ self.add_primitive(
clip_and_scroll,
&info,
- center,
- start_radius,
- end_radius,
- ratio_xy,
- stops,
- extend_mode,
- gradient_index,
- stretch_size,
+ Vec::new(),
+ PrimitiveContainer::Brush(prim),
);
}
pub fn add_text(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
run_offset: LayoutVector2D,
prim_info: &LayoutPrimitiveInfo,
@@ -2203,82 +2102,16 @@ pub fn build_scene(config: &FrameBuilder
BuiltScene {
scene: new_scene,
frame_builder,
clip_scroll_tree,
removed_pipelines: request.removed_pipelines,
}
}
-trait PrimitiveInfoTiler {
- fn decompose(
- &self,
- tile_size: LayoutSize,
- tile_spacing: LayoutSize,
- max_prims: usize,
- ) -> Vec<LayoutPrimitiveInfo>;
-}
-
-impl PrimitiveInfoTiler for LayoutPrimitiveInfo {
- fn decompose(
- &self,
- tile_size: LayoutSize,
- tile_spacing: LayoutSize,
- max_prims: usize,
- ) -> Vec<LayoutPrimitiveInfo> {
- let mut prims = Vec::new();
- let tile_repeat = tile_size + tile_spacing;
-
- if tile_repeat.width <= 0.0 ||
- tile_repeat.height <= 0.0 {
- return prims;
- }
-
- if tile_repeat.width < self.rect.size.width ||
- tile_repeat.height < self.rect.size.height {
- let clip_rect = self.clip_rect
- .intersection(&self.rect)
- .unwrap_or_else(LayoutRect::zero);
- let rect_p0 = self.rect.origin;
- let rect_p1 = self.rect.bottom_right();
-
- let mut y0 = rect_p0.y;
- while y0 < rect_p1.y {
- let mut x0 = rect_p0.x;
-
- while x0 < rect_p1.x {
- prims.push(LayoutPrimitiveInfo {
- rect: LayoutRect::new(
- LayoutPoint::new(x0, y0),
- tile_size,
- ),
- clip_rect,
- is_backface_visible: self.is_backface_visible,
- tag: self.tag,
- });
-
- // Mostly a safety against a crazy number of primitives
- // being generated. If we exceed that amount, just bail
- // out and only draw the maximum amount.
- if prims.len() > max_prims {
- warn!("too many prims found due to repeat/tile. dropping extra prims!");
- return prims;
- }
-
- x0 += tile_repeat.width;
- }
-
- y0 += tile_repeat.height;
- }
- }
-
- prims
- }
-}
-
/// Properties of a stacking context that are maintained
/// during creation of the scene. These structures are
/// not persisted after the initial scene build.
struct FlattenedStackingContext {
/// Pipeline this stacking context belongs to.
pipeline_id: PipelineId,
/// Filters / mix-blend-mode effects
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -158,17 +158,17 @@ pub struct PicturePrimitive {
}
impl PicturePrimitive {
pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
match self.composite_mode {
Some(PictureCompositeMode::Filter(ref mut filter)) => {
match *filter {
FilterOp::Opacity(ref binding, ref mut value) => {
- *value = properties.resolve_float(binding, *value);
+ *value = properties.resolve_float(binding);
}
_ => {}
}
filter.is_visible()
}
_ => true,
}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -226,17 +226,17 @@ impl OpacityBinding {
// Resolve the current value of each opacity binding, and
// store that as a single combined opacity. Returns true
// if the opacity value changed from last time.
pub fn update(&mut self, scene_properties: &SceneProperties) -> bool {
let mut new_opacity = 1.0;
for binding in &self.bindings {
- let opacity = scene_properties.resolve_float(binding, 1.0);
+ let opacity = scene_properties.resolve_float(binding);
new_opacity = new_opacity * opacity;
}
let changed = new_opacity != self.current;
self.current = new_opacity;
changed
}
@@ -245,16 +245,21 @@ impl OpacityBinding {
#[derive(Debug)]
pub struct VisibleImageTile {
pub tile_offset: TileOffset,
pub handle: GpuCacheHandle,
pub edge_flags: EdgeAaSegmentMask,
}
#[derive(Debug)]
+pub struct VisibleGradientTile {
+ pub handle: GpuCacheHandle,
+}
+
+#[derive(Debug)]
pub enum BrushKind {
Solid {
color: ColorF,
opacity_binding: OpacityBinding,
},
Clear,
Picture {
pic_index: PictureIndex,
@@ -280,26 +285,30 @@ pub enum BrushKind {
gradient_index: CachedGradientIndex,
stops_range: ItemRange<GradientStop>,
extend_mode: ExtendMode,
center: LayoutPoint,
start_radius: f32,
end_radius: f32,
ratio_xy: f32,
stretch_size: LayoutSize,
+ tile_spacing: LayoutSize,
+ visible_tiles: Vec<VisibleGradientTile>,
},
LinearGradient {
gradient_index: CachedGradientIndex,
stops_range: ItemRange<GradientStop>,
stops_count: usize,
extend_mode: ExtendMode,
reverse_stops: bool,
start_point: LayoutPoint,
end_point: LayoutPoint,
stretch_size: LayoutSize,
+ tile_spacing: LayoutSize,
+ visible_tiles: Vec<VisibleGradientTile>,
},
Border {
request: ImageRequest,
},
}
impl BrushKind {
fn supports_segments(&self) -> bool {
@@ -1418,17 +1427,17 @@ impl PrimitiveStore {
prim_index: PrimitiveIndex,
prim_run_context: &PrimitiveRunContext,
pic_state_for_children: PictureState,
pic_context: &PictureContext,
pic_state: &mut PictureState,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
) {
- let mut is_tiled_image = false;
+ let mut is_tiled = false;
let metadata = &mut self.cpu_metadata[prim_index.0];
#[cfg(debug_assertions)]
{
metadata.prepared_frame_id = frame_state.render_tasks.frame_id();
}
match metadata.prim_kind {
PrimitiveKind::Border => {}
@@ -1462,46 +1471,46 @@ impl PrimitiveStore {
let image_properties = frame_state
.resource_cache
.get_image_properties(request.key);
// Set if we need to request the source image from the cache this frame.
if let Some(image_properties) = image_properties {
*current_epoch = image_properties.epoch;
- is_tiled_image = image_properties.tiling.is_some();
+ is_tiled = image_properties.tiling.is_some();
// If the opacity changed, invalidate the GPU cache so that
// the new color for this primitive gets uploaded.
if opacity_binding.update(frame_context.scene_properties) {
frame_state.gpu_cache.invalidate(&mut metadata.gpu_location);
}
// Update opacity for this primitive to ensure the correct
// batching parameters are used.
metadata.opacity.is_opaque =
image_properties.descriptor.is_opaque &&
opacity_binding.current == 1.0;
- if *tile_spacing != LayoutSize::zero() && !is_tiled_image {
+ if *tile_spacing != LayoutSize::zero() && !is_tiled {
*source = ImageSource::Cache {
// Size in device-pixels we need to allocate in render task cache.
size: DeviceIntSize::new(
image_properties.descriptor.width as i32,
image_properties.descriptor.height as i32
),
handle: None,
};
}
// Work out whether this image is a normal / simple type, or if
// we need to pre-render it to the render task cache.
if let Some(rect) = sub_rect {
// We don't properly support this right now.
- debug_assert!(!is_tiled_image);
+ debug_assert!(!is_tiled);
*source = ImageSource::Cache {
// Size in device-pixels we need to allocate in render task cache.
size: rect.size,
handle: None,
};
}
let mut request_source_image = false;
@@ -1693,39 +1702,117 @@ impl PrimitiveStore {
image_properties.descriptor.is_opaque;
frame_state.resource_cache.request_image(
request,
frame_state.gpu_cache,
);
}
}
- BrushKind::RadialGradient { gradient_index, stops_range, .. } => {
- let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
- if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
- let gradient_builder = GradientGpuBlockBuilder::new(
- stops_range,
- pic_context.display_list,
- );
- gradient_builder.build(
- false,
- &mut request,
+ BrushKind::RadialGradient {
+ gradient_index,
+ stops_range,
+ center,
+ start_radius,
+ end_radius,
+ ratio_xy,
+ extend_mode,
+ stretch_size,
+ tile_spacing,
+ ref mut visible_tiles,
+ ..
+ } => {
+ build_gradient_stops_request(
+ gradient_index,
+ stops_range,
+ false,
+ frame_state,
+ pic_context,
+ );
+
+ if tile_spacing != LayoutSize::zero() {
+ is_tiled = true;
+
+ decompose_repeated_primitive(
+ visible_tiles,
+ metadata,
+ &stretch_size,
+ &tile_spacing,
+ prim_run_context,
+ frame_context,
+ frame_state,
+ &mut |rect, clip_rect, mut request| {
+ request.push(*rect);
+ request.push(*clip_rect);
+ request.push([
+ center.x,
+ center.y,
+ start_radius,
+ end_radius,
+ ]);
+ request.push([
+ ratio_xy,
+ pack_as_float(extend_mode as u32),
+ stretch_size.width,
+ stretch_size.height,
+ ]);
+ request.write_segment(*rect, [0.0; 4]);
+ },
);
}
}
- BrushKind::LinearGradient { gradient_index, stops_range, reverse_stops, .. } => {
- let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
- if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
- let gradient_builder = GradientGpuBlockBuilder::new(
- stops_range,
- pic_context.display_list,
- );
- gradient_builder.build(
- reverse_stops,
- &mut request,
+ BrushKind::LinearGradient {
+ gradient_index,
+ stops_range,
+ reverse_stops,
+ start_point,
+ end_point,
+ extend_mode,
+ stretch_size,
+ tile_spacing,
+ ref mut visible_tiles,
+ ..
+ } => {
+
+ build_gradient_stops_request(
+ gradient_index,
+ stops_range,
+ reverse_stops,
+ frame_state,
+ pic_context,
+ );
+
+ if tile_spacing != LayoutSize::zero() {
+ is_tiled = true;
+
+ decompose_repeated_primitive(
+ visible_tiles,
+ metadata,
+ &stretch_size,
+ &tile_spacing,
+ prim_run_context,
+ frame_context,
+ frame_state,
+ &mut |rect, clip_rect, mut request| {
+ request.push(*rect);
+ request.push(*clip_rect);
+ request.push([
+ start_point.x,
+ start_point.y,
+ end_point.x,
+ end_point.y,
+ ]);
+ request.push([
+ pack_as_float(extend_mode as u32),
+ stretch_size.width,
+ stretch_size.height,
+ 0.0,
+ ]);
+ request.write_segment(*rect, [0.0; 4]);
+ }
);
}
}
BrushKind::Picture { pic_index, .. } => {
let pic = &mut self.pictures[pic_index.0];
pic.prepare_for_render(
prim_index,
metadata,
@@ -1746,17 +1833,17 @@ impl PrimitiveStore {
frame_state.gpu_cache.invalidate(&mut metadata.gpu_location);
}
}
BrushKind::Clear => {}
}
}
}
- if is_tiled_image {
+ if is_tiled {
// we already requested each tile's gpu data.
return;
}
// Mark this GPU resource as required for this frame.
if let Some(mut request) = frame_state.gpu_cache.request(&mut metadata.gpu_location) {
// has to match VECS_PER_BRUSH_PRIM
request.push(metadata.local_rect);
@@ -2475,16 +2562,90 @@ impl PrimitiveStore {
}
}
}
result
}
}
+fn build_gradient_stops_request(
+ gradient_index: CachedGradientIndex,
+ stops_range: ItemRange<GradientStop>,
+ reverse_stops: bool,
+ frame_state: &mut FrameBuildingState,
+ pic_context: &PictureContext
+) {
+ let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
+ if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
+ let gradient_builder = GradientGpuBlockBuilder::new(
+ stops_range,
+ pic_context.display_list,
+ );
+ gradient_builder.build(
+ reverse_stops,
+ &mut request,
+ );
+ }
+}
+
+fn decompose_repeated_primitive(
+ visible_tiles: &mut Vec<VisibleGradientTile>,
+ metadata: &mut PrimitiveMetadata,
+ stretch_size: &LayoutSize,
+ tile_spacing: &LayoutSize,
+ prim_run_context: &PrimitiveRunContext,
+ frame_context: &FrameBuildingContext,
+ frame_state: &mut FrameBuildingState,
+ callback: &mut FnMut(&LayoutRect, &LayoutRect, GpuDataRequest),
+) {
+ visible_tiles.clear();
+
+ // Tighten the clip rect because decomposing the repeated image can
+ // produce primitives that are partially covering the original image
+ // rect and we want to clip these extra parts out.
+ let tight_clip_rect = metadata.local_clip_rect.intersection(&metadata.local_rect).unwrap();
+
+ let visible_rect = compute_conservative_visible_rect(
+ prim_run_context,
+ frame_context,
+ &tight_clip_rect
+ );
+ let stride = *stretch_size + *tile_spacing;
+
+ for_each_repetition(
+ &metadata.local_rect,
+ &visible_rect,
+ &stride,
+ &mut |origin, _| {
+
+ let mut handle = GpuCacheHandle::new();
+ if let Some(request) = frame_state.gpu_cache.request(&mut handle) {
+ let rect = LayoutRect {
+ origin: *origin,
+ size: *stretch_size,
+ };
+
+ callback(&rect, &tight_clip_rect, request);
+ }
+
+ visible_tiles.push(VisibleGradientTile { handle });
+ }
+ );
+
+ if visible_tiles.is_empty() {
+ // At this point if we don't have tiles to show it means we could probably
+ // have done a better a job at culling during an earlier stage.
+ // Clearing the screen rect has the effect of "culling out" the primitive
+ // from the point of view of the batch builder, and ensures we don't hit
+ // assertions later on because we didn't request any image.
+ metadata.screen_rect = None;
+ }
+}
+
fn compute_conservative_visible_rect(
prim_run_context: &PrimitiveRunContext,
frame_context: &FrameBuildingContext,
local_clip_rect: &LayoutRect,
) -> LayoutRect {
let world_screen_rect = prim_run_context
.clip_chain.combined_outer_screen_rect
.to_f32() / frame_context.device_pixel_scale;
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -49,46 +49,37 @@ impl SceneProperties {
/// Get the current value for a transform property.
pub fn resolve_layout_transform(
&self,
property: &PropertyBinding<LayoutTransform>,
) -> LayoutTransform {
match *property {
PropertyBinding::Value(value) => value,
- PropertyBinding::Binding(ref key) => {
+ PropertyBinding::Binding(ref key, v) => {
self.transform_properties
.get(&key.id)
.cloned()
- .unwrap_or_else(|| {
- warn!("Property binding has an invalid value.");
- debug!("key={:?}", key);
- LayoutTransform::identity()
- })
+ .unwrap_or(v)
}
}
}
/// Get the current value for a float property.
pub fn resolve_float(
&self,
- property: &PropertyBinding<f32>,
- default_value: f32
+ property: &PropertyBinding<f32>
) -> f32 {
match *property {
PropertyBinding::Value(value) => value,
- PropertyBinding::Binding(ref key) => {
+ PropertyBinding::Binding(ref key, v) => {
self.float_properties
.get(&key.id)
.cloned()
- .unwrap_or_else(|| {
- warn!("Property binding has an invalid value.");
- debug!("key={:?}", key);
- default_value
- })
+ .unwrap_or(v)
}
}
}
}
/// A representation of the layout within the display port for a given document or iframe.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -1073,34 +1073,31 @@ impl<T> PropertyBindingKey<T> {
_phantom: PhantomData,
}
}
}
/// A binding property can either be a specific value
/// (the normal, non-animated case) or point to a binding location
/// to fetch the current value from.
+/// Note that Binding has also a non-animated value, the value is
+/// used for the case where the animation is still in-delay phase
+/// (i.e. the animation doesn't produce any animation values).
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum PropertyBinding<T> {
Value(T),
- Binding(PropertyBindingKey<T>),
+ Binding(PropertyBindingKey<T>, T),
}
impl<T> From<T> for PropertyBinding<T> {
fn from(value: T) -> PropertyBinding<T> {
PropertyBinding::Value(value)
}
}
-impl<T> From<PropertyBindingKey<T>> for PropertyBinding<T> {
- fn from(key: PropertyBindingKey<T>) -> PropertyBinding<T> {
- PropertyBinding::Binding(key)
- }
-}
-
/// The current value of an animated property. This is
/// supplied by the calling code.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub struct PropertyValue<T> {
pub key: PropertyBindingKey<T>,
pub value: T,
}
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-4811a6e7c06f9dd1b4056e5f5e66983842983ba0
+9d20df4e76e3b19c569fd89965f70a2c278ff0c8
--- a/gfx/wrench/src/blob.rs
+++ b/gfx/wrench/src/blob.rs
@@ -20,16 +20,22 @@ fn deserialize_blob(blob: &[u8]) -> Resu
let mut iter = blob.iter();
return match (iter.next(), iter.next(), iter.next(), iter.next()) {
(Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(ColorU::new(r, g, b, a)),
(Some(&a), None, None, None) => Ok(ColorU::new(a, a, a, a)),
_ => Err(()),
};
}
+// perform floor((x * a) / 255. + 0.5) see "Three wrongs make a right" for deriviation
+fn premul(x: u8, a: u8) -> u8 {
+ let t = (x as u32) * (a as u32) + 128;
+ ((t + (t >> 8)) >> 8) as u8
+}
+
// This is the function that applies the deserialized drawing commands and generates
// actual image data.
fn render_blob(
color: ColorU,
descriptor: &BlobImageDescriptor,
tile: Option<(TileSize, TileOffset)>,
dirty_rect: Option<DeviceUintRect>,
) -> BlobImageResult {
@@ -68,20 +74,21 @@ fn render_blob(
} else {
0
};
// ..nested in the per-tile checkerboard pattern
let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
match descriptor.format {
ImageFormat::BGRA8 => {
- texels[((y * descriptor.width + x) * 4 + 0) as usize] = color.b * checker + tc;
- texels[((y * descriptor.width + x) * 4 + 1) as usize] = color.g * checker + tc;
- texels[((y * descriptor.width + x) * 4 + 2) as usize] = color.r * checker + tc;
- texels[((y * descriptor.width + x) * 4 + 3) as usize] = color.a * checker + tc;
+ let a = color.a * checker + tc;
+ texels[((y * descriptor.width + x) * 4 + 0) as usize] = premul(color.b * checker + tc, a);
+ texels[((y * descriptor.width + x) * 4 + 1) as usize] = premul(color.g * checker + tc, a);
+ texels[((y * descriptor.width + x) * 4 + 2) as usize] = premul(color.r * checker + tc, a);
+ texels[((y * descriptor.width + x) * 4 + 3) as usize] = a;
}
ImageFormat::R8 => {
texels[(y * descriptor.width + x) as usize] = color.a * checker + tc;
}
_ => {
return Err(BlobImageError::Other(
format!("Unsupported image format {:?}", descriptor.format),
));
--- a/gfx/wrench/src/scene.rs
+++ b/gfx/wrench/src/scene.rs
@@ -40,37 +40,31 @@ impl SceneProperties {
) -> LayoutTransform {
let property = match *property {
Some(property) => property,
None => return LayoutTransform::identity(),
};
match property {
PropertyBinding::Value(matrix) => matrix,
- PropertyBinding::Binding(ref key) => self.transform_properties
+ PropertyBinding::Binding(ref key, v) => self.transform_properties
.get(&key.id)
.cloned()
- .unwrap_or_else(|| {
- println!("Property binding {:?} has an invalid value.", key);
- LayoutTransform::identity()
- }),
+ .unwrap_or(v),
}
}
/// Get the current value for a float property.
- pub fn resolve_float(&self, property: &PropertyBinding<f32>, default_value: f32) -> f32 {
+ pub fn resolve_float(&self, property: &PropertyBinding<f32>) -> f32 {
match *property {
PropertyBinding::Value(value) => value,
- PropertyBinding::Binding(ref key) => self.float_properties
+ PropertyBinding::Binding(ref key, v) => self.float_properties
.get(&key.id)
.cloned()
- .unwrap_or_else(|| {
- println!("Property binding {:?} has an invalid value.", key);
- default_value
- }),
+ .unwrap_or(v),
}
}
}
/// A representation of the layout within the display port for a given document or iframe.
#[derive(Debug)]
pub struct ScenePipeline {
pub epoch: Epoch,
--- a/gfx/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wrench/src/yaml_frame_writer.rs
@@ -218,17 +218,17 @@ fn write_stacking_context(
FilterOp::Blur(x) => { filters.push(Yaml::String(format!("blur({})", x))) }
FilterOp::Brightness(x) => { filters.push(Yaml::String(format!("brightness({})", x))) }
FilterOp::Contrast(x) => { filters.push(Yaml::String(format!("contrast({})", x))) }
FilterOp::Grayscale(x) => { filters.push(Yaml::String(format!("grayscale({})", x))) }
FilterOp::HueRotate(x) => { filters.push(Yaml::String(format!("hue-rotate({})", x))) }
FilterOp::Invert(x) => { filters.push(Yaml::String(format!("invert({})", x))) }
FilterOp::Opacity(x, _) => {
filters.push(Yaml::String(format!("opacity({})",
- properties.resolve_float(&x, 1.0))))
+ properties.resolve_float(&x))))
}
FilterOp::Saturate(x) => { filters.push(Yaml::String(format!("saturate({})", x))) }
FilterOp::Sepia(x) => { filters.push(Yaml::String(format!("sepia({})", x))) }
FilterOp::DropShadow(offset, blur, color) => {
filters.push(Yaml::String(format!("drop-shadow([{},{}],{},[{}])",
offset.x, offset.y,
blur,
color_to_string(color))))