Bug 1896503 - Implement FilterInstance code to send SVG filter graph to WebRender r=mstange,gw

Differential Revision: https://phabricator.services.mozilla.com/D174208
This commit is contained in:
Ashley Hale 2024-06-24 23:45:05 +00:00
Родитель 69918535dc
Коммит cac93eda00
25 изменённых файлов: 1641 добавлений и 271 удалений

Просмотреть файл

@ -795,9 +795,6 @@ pub struct FilterGraphNodeKey {
/// sRGB texture pixel colors on load and convert back on store, for correct
/// interpolation
pub linear: bool,
/// padding for output rect if we need a border to get correct clamping, or
/// to account for larger final subregion than source rect (see bug 1869672)
pub inflate: i16,
/// virtualized picture input binding 1 (i.e. texture source), typically
/// this is used, but certain filters do not use it
pub inputs: Vec<FilterGraphPictureReferenceKey>,
@ -811,7 +808,6 @@ impl From<FilterGraphNode> for FilterGraphNodeKey {
FilterGraphNodeKey{
kept_by_optimizer: node.kept_by_optimizer,
linear: node.linear,
inflate: node.inflate,
inputs: node.inputs.into_iter().map(|node| {node.into()}).collect(),
subregion: [
Au::from_f32_px(node.subregion.min.x),

Просмотреть файл

@ -1640,7 +1640,7 @@ impl RenderTask {
data_stores: &mut DataStores,
uv_rect_kind: UvRectKind,
original_task_id: RenderTaskId,
_surface_rects_task_size: DeviceIntSize,
surface_rects_task_size: DeviceIntSize,
surface_rects_clipped: DeviceRect,
surface_rects_clipped_local: PictureRect,
) -> RenderTaskId {
@ -2256,39 +2256,29 @@ impl RenderTask {
},
}
// If this is the output node, we have to match the provided filter
// subregion as the primitive it is applied to is already placed (it
// was calculated in get_surface_rects using get_coverage_svgfe).
let node_subregion = match is_output {
true => output_subregion,
false => used_subregion,
};
// If this is the output node, apply the output clip.
let node_inflate = node.inflate;
let mut create_output_task = false;
if is_output {
// If we're drawing a subregion that encloses output_subregion
// we can just crop the node to output_subregion.
if used_subregion.to_i32().contains_box(&output_rect) {
used_subregion = output_subregion;
} else {
// We'll have to create an extra blit task after this task
// so that there is transparent black padding around it.
create_output_task = true;
}
}
// Convert subregion from layout pixels to integer device pixels and
// then calculate size afterwards so it reflects the used pixel area
//
// In case of the output node we preserve the exact filter_subregion
// task size.
//
// This can be an empty rect if the source_subregion invalidation
// rect didn't request any pixels of this node, but we can't skip
// creating tasks that have no size because they would leak in the
// render task graph with no consumers
let node_task_rect =
match is_output {
true => output_rect,
false => node_subregion.to_i32(),
};
// SVG spec requires that a later node sampling pixels outside
// this node's subregion will receive a transparent black color
// for those samples, we achieve this by adding a 1 pixel border
// around the target rect, which works fine with the clamping of the
// texture fetch in the shader, and to account for the offset we
// have to make a UvRectKind::Quad mapping for later nodes to use
// when sampling this output, if they use feOffset or have a
// larger target rect those samples will be clamped to the
// transparent black border and thus meet spec.
let node_task_rect: DeviceIntRect = used_subregion.to_i32().cast_unit();
let mut node_task_size = node_task_rect.size().cast_unit();
// We have to limit the render target sizes we're asking for on the
@ -2300,22 +2290,31 @@ impl RenderTask {
// space. Blurs will do this same logic if their intermediate is
// too large. We use a simple halving calculation here so that
// pixel alignment is still vaguely sensible.
while node_task_size.width as usize + node.inflate as usize * 2 > MAX_SURFACE_SIZE ||
node_task_size.height as usize + node.inflate as usize * 2 > MAX_SURFACE_SIZE {
while node_task_size.width as usize + node_inflate as usize * 2 > MAX_SURFACE_SIZE ||
node_task_size.height as usize + node_inflate as usize * 2 > MAX_SURFACE_SIZE {
node_task_size.width >>= 1;
node_task_size.height >>= 1;
}
// Add the inflate border
node_task_size.width += node.inflate as i32 * 2;
node_task_size.height += node.inflate as i32 * 2;
// SVG spec requires that a later node sampling pixels outside
// this node's subregion will receive a transparent black color
// for those samples, we achieve this by adding a 1 pixel border
// around the target rect, which works fine with the clamping of the
// texture fetch in the shader, and to account for the offset we
// have to make a UvRectKind::Quad mapping for later nodes to use
// when sampling this output, if they use feOffset or have a
// larger target rect those samples will be clamped to the
// transparent black border and thus meet spec.
node_task_size.width += node_inflate as i32 * 2;
node_task_size.height += node_inflate as i32 * 2;
// Make the uv_rect_kind for this node's task to use, this matters
// only on the final node because we don't use it internally
let node_uv_rect_kind =
uv_rect_kind_for_task_size(node_task_size, node.inflate);
uv_rect_kind_for_task_size(node_task_size, node_inflate);
// Create task for this node
let task_id;
let mut task_id;
match op {
FilterGraphOp::SVGFEGaussianBlur { std_deviation_x, std_deviation_y } => {
// Note: wrap_prim_with_filters copies the SourceGraphic to
@ -2430,7 +2429,7 @@ impl RenderTask {
node: FilterGraphNode{
kept_by_optimizer: true,
linear: node.linear,
inflate: node.inflate,
inflate: node_inflate,
inputs: [
FilterGraphPictureReference{
buffer_id: blur_input.buffer_id,
@ -2440,7 +2439,7 @@ impl RenderTask {
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
}].to_vec(),
subregion: node_subregion,
subregion: used_subregion,
},
op: FilterGraphOp::SVGFEIdentity,
content_origin: DevicePoint::zero(),
@ -2575,7 +2574,7 @@ impl RenderTask {
node: FilterGraphNode{
kept_by_optimizer: true,
linear: node.linear,
inflate: node.inflate,
inflate: node_inflate,
inputs: [
// Original picture
*blur_input,
@ -2588,7 +2587,7 @@ impl RenderTask {
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
}].to_vec(),
subregion: node_subregion,
subregion: used_subregion,
},
op: FilterGraphOp::SVGFEDropShadow{
color,
@ -2618,7 +2617,7 @@ impl RenderTask {
node: FilterGraphNode{
kept_by_optimizer: true,
linear: node.linear,
inflate: node.inflate,
inflate: node_inflate,
inputs: [
FilterGraphPictureReference{
buffer_id: FilterOpGraphPictureBufferId::None,
@ -2635,7 +2634,7 @@ impl RenderTask {
target_padding: LayoutRect::zero(),
}
].to_vec(),
subregion: node_subregion,
subregion: used_subregion,
},
op: op.clone(),
content_origin: DevicePoint::zero(),
@ -2661,8 +2660,8 @@ impl RenderTask {
kept_by_optimizer: true,
linear: node.linear,
inputs: node_inputs.iter().map(|input| {input.0}).collect(),
subregion: node_subregion,
inflate: node.inflate,
subregion: used_subregion,
inflate: node_inflate,
},
op: op.clone(),
content_origin: DevicePoint::zero(),
@ -2693,8 +2692,8 @@ impl RenderTask {
kept_by_optimizer: true,
linear: node.linear,
inputs: node_inputs.iter().map(|input| {input.0}).collect(),
subregion: node_subregion,
inflate: node.inflate,
subregion: used_subregion,
inflate: node_inflate,
},
op: op.clone(),
content_origin: DevicePoint::zero(),
@ -2720,9 +2719,44 @@ impl RenderTask {
// to look them up quickly, since nodes can only depend on previous
// nodes in the same list
task_by_buffer_id[filter_index] = task_id;
subregion_by_buffer_id[filter_index] = node_subregion;
subregion_by_buffer_id[filter_index] = used_subregion;
if is_output {
// The final task we create is the output picture.
output_task_id = task_id;
if create_output_task {
// If the final node subregion is smaller than the output rect,
// we need to pad it with transparent black to match SVG spec,
// as the output task rect is larger than the invalidated area,
// ideally the origin and size of the picture we return should
// be used instead of the get_rect result for sizing geometry,
// as it would allow us to produce a much smaller rect.
let output_uv_rect_kind =
uv_rect_kind_for_task_size(surface_rects_task_size, 0);
task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic(
surface_rects_task_size,
RenderTaskKind::SVGFENode(
SVGFEFilterTask{
node: FilterGraphNode{
kept_by_optimizer: true,
linear: false,
inputs: [FilterGraphPictureReference{
buffer_id: FilterOpGraphPictureBufferId::None,
subregion: used_subregion,
offset: LayoutVector2D::zero(),
inflate: node_inflate,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
}].to_vec(),
subregion: output_subregion,
inflate: 0,
},
op: FilterGraphOp::SVGFEIdentity,
content_origin: surface_rects_clipped.min,
extra_gpu_cache_handle: None,
}
),
).with_uv_rect_kind(output_uv_rect_kind));
frame_state.rg_builder.add_dependency(task_id, output_task_id);
output_task_id = task_id;
}
}

Просмотреть файл

@ -3795,6 +3795,9 @@ impl<'a> SceneBuilder<'a> {
// For each filter, create a new image with that composite mode.
let mut current_filter_data_index = 0;
// Check if the filter chain is actually an SVGFE filter graph DAG
//
// TODO: We technically could translate all CSS filters to SVGFE here if
// we want to reduce redundant code.
if let Some(Filter::SVGGraphNode(..)) = filter_ops.first() {
// The interesting parts of the handling of SVG filters are:
// * scene_building.rs : wrap_prim_with_filters (you are here)
@ -3808,9 +3811,6 @@ impl<'a> SceneBuilder<'a> {
// Easily tunable for debugging proper handling of inflated rects,
// this should normally be 1
const SVGFE_INFLATE: i16 = 1;
// Easily tunable for debugging proper handling of inflated rects,
// this should normally be 0
const SVGFE_INFLATE_OUTPUT: i16 = 0;
// Validate inputs to all filters.
//
@ -3846,14 +3846,14 @@ impl<'a> SceneBuilder<'a> {
let mut filters: Vec<(FilterGraphNode, FilterGraphOp)> = Vec::new();
filters.reserve(BUFFER_LIMIT);
for (original_id, parsefilter) in filter_ops.iter().enumerate() {
match parsefilter {
Filter::SVGGraphNode(parsenode, op) => {
if filters.len() >= BUFFER_LIMIT {
// If the DAG is too large we drop it entirely, the spec
// allows this.
return source;
}
if filters.len() >= BUFFER_LIMIT {
// If the DAG is too large to process, the spec requires
// that we drop all filters and display source image as-is.
return source;
}
let newfilter = match parsefilter {
Filter::SVGGraphNode(parsenode, op) => {
// We need to offset the subregion by the stacking context
// offset or we'd be in the wrong coordinate system, prims
// are already offset by this same amount.
@ -3923,7 +3923,7 @@ impl<'a> SceneBuilder<'a> {
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} |
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {
assert!(remapped_inputs.len() == 0);
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
}
FilterGraphOp::SVGFEColorMatrix{..} |
FilterGraphOp::SVGFEIdentity |
@ -3932,7 +3932,7 @@ impl<'a> SceneBuilder<'a> {
FilterGraphOp::SVGFEToAlpha => {
assert!(remapped_inputs.len() == 1);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
}
FilterGraphOp::SVGFEComponentTransfer => {
assert!(remapped_inputs.len() == 1);
@ -3977,7 +3977,7 @@ impl<'a> SceneBuilder<'a> {
.intern(&filter_data_key, || ());
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), FilterGraphOp::SVGFEComponentTransferInterned{handle, creates_pixels}));
(newnode.clone(), FilterGraphOp::SVGFEComponentTransferInterned{handle, creates_pixels})
}
FilterGraphOp::SVGFEComponentTransferInterned{..} => unreachable!(),
FilterGraphOp::SVGFETile => {
@ -3988,7 +3988,7 @@ impl<'a> SceneBuilder<'a> {
remapped_inputs[0].target_padding =
LayoutRect::max_rect();
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
}
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{kernel_unit_length_x, kernel_unit_length_y, ..} |
@ -4010,7 +4010,7 @@ impl<'a> SceneBuilder<'a> {
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
},
FilterGraphOp::SVGFEDiffuseLightingDistant{kernel_unit_length_x, kernel_unit_length_y, ..} |
FilterGraphOp::SVGFEDiffuseLightingPoint{kernel_unit_length_x, kernel_unit_length_y, ..} |
@ -4035,7 +4035,7 @@ impl<'a> SceneBuilder<'a> {
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
},
FilterGraphOp::SVGFEDisplacementMap { scale, .. } => {
assert!(remapped_inputs.len() == 2);
@ -4060,7 +4060,7 @@ impl<'a> SceneBuilder<'a> {
remapped_inputs[1].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
},
FilterGraphOp::SVGFEDropShadow{ dx, dy, std_deviation_x, std_deviation_y, .. } => {
assert!(remapped_inputs.len() == 1);
@ -4090,7 +4090,7 @@ impl<'a> SceneBuilder<'a> {
)
);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
},
FilterGraphOp::SVGFEGaussianBlur{std_deviation_x, std_deviation_y} => {
assert!(remapped_inputs.len() == 1);
@ -4107,7 +4107,7 @@ impl<'a> SceneBuilder<'a> {
remapped_inputs[0].target_padding
.inflate(padding.width, padding.height);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode.clone(), op.clone())
}
FilterGraphOp::SVGFEBlendColor |
FilterGraphOp::SVGFEBlendColorBurn |
@ -4134,62 +4134,59 @@ impl<'a> SceneBuilder<'a> {
FilterGraphOp::SVGFECompositeXOR => {
assert!(remapped_inputs.len() == 2);
newnode.inputs = remapped_inputs;
filters.push((newnode.clone(), op.clone()));
(newnode, op.clone())
}
}
// Set the reference remapping for the last (or only) node
// that we just pushed
let id = (filters.len() - 1) as i16;
if let Some(pic) = reference_for_buffer_id.get_mut(original_id as usize) {
*pic = FilterGraphPictureReference {
buffer_id: FilterOpGraphPictureBufferId::BufferId(id),
subregion: newnode.subregion,
offset: LayoutVector2D::zero(),
inflate: newnode.inflate,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
};
}
}
Filter::Opacity(valuebinding, value) => {
// Opacity filter is sometimes appended by
// wr_dp_push_stacking_context before we get here,
// convert to SVGFEOpacity in the graph. Note that
// linear is set to false because it has no meaning for
// opacity (which scales all of the RGBA uniformly).
let pic = reference_for_buffer_id[original_id as usize - 1];
(
FilterGraphNode {
kept_by_optimizer: false,
linear: false,
inflate: SVGFE_INFLATE,
inputs: [pic].to_vec(),
subregion: pic.subregion,
},
FilterGraphOp::SVGFEOpacity{
valuebinding: *valuebinding,
value: *value,
},
)
}
_ => {
panic!("wrap_prim_with_filters: Mixed SVG and CSS filters?")
log!(Level::Warn, "wrap_prim_with_filters: unexpected filter after SVG filters filter[{:?}]={:?}", original_id, parsefilter);
// If we can't figure out how to process the graph, spec
// requires that we drop all filters and display source
// image as-is.
return source;
}
}
};
let id = filters.len();
filters.push(newfilter);
// Set the reference remapping for the last (or only) node
// that we just pushed
reference_for_buffer_id[original_id] = FilterGraphPictureReference {
buffer_id: FilterOpGraphPictureBufferId::BufferId(id as i16),
subregion: filters[id].0.subregion,
offset: LayoutVector2D::zero(),
inflate: filters[id].0.inflate,
source_padding: LayoutRect::zero(),
target_padding: LayoutRect::zero(),
};
}
// Push a special output node at the end, this will correctly handle
// the final subregion, which may not have the same bounds as the
// surface it is being blitted into, so it needs to properly handle
// the cropping and UvRectKind, it also has no inflate.
if filters.len() >= BUFFER_LIMIT {
// If the DAG is too large we drop it entirely
// If the DAG is too large to process, the spec requires
// that we drop all filters and display source image as-is.
return source;
}
let mut outputnode = FilterGraphNode {
kept_by_optimizer: true,
linear: false,
inflate: SVGFE_INFLATE_OUTPUT,
inputs: Vec::new(),
subregion: LayoutRect::max_rect(),
};
outputnode.inputs.push(reference_for_buffer_id[filter_ops.len() - 1]);
filters.push((
outputnode,
FilterGraphOp::SVGFEIdentity,
));
// We want to optimize the filter DAG and then wrap it in a single
// picture, we will use a custom RenderTask method to process the
// DAG later, there's not really an easy way to keep it as a series
// of pictures like CSS filters use.
//
// The main optimization we can do here is looking for feOffset
// filters we can merge away - because all of the node inputs
// support offset capability implicitly. We can also remove no-op
// filters (identity) if Gecko produced any.
//
// TODO: optimize the graph here
// Mark used graph nodes, starting at the last graph node, since
// this is a DAG in sorted order we can just iterate backwards and
@ -4223,8 +4220,8 @@ impl<'a> SceneBuilder<'a> {
}
}
// Validate the DAG nature of the graph again - if we find anything
// wrong here it means the above code is bugged.
// Validate the DAG nature of the graph - if we find anything wrong
// here it means the above code is bugged.
let mut invalid_dag = false;
for (id, (node, _op)) in filters.iter().enumerate() {
for input in &node.inputs {

Просмотреть файл

@ -2299,8 +2299,32 @@ void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder, gfxContext* aCtx,
gfx::Matrix5x4* colorMatrix =
nsDocShell::Cast(docShell)->GetColorMatrix();
if (colorMatrix) {
wrFilters.filters.AppendElement(
wr::FilterOp::ColorMatrix(colorMatrix->components));
// Note: This color matrix was added here in for accessibility in
// https://bugzilla.mozilla.org/show_bug.cgi?id=1431466 , it could be
// done with regular SVG in the document as long as it is accelerated,
// and it's probably best to do this in linearRGB, now that it is
// feasible to do so
// TODO(ahale): Make sure to test this works correctly before enabling
if (StaticPrefs::gfx_webrender_svg_filter_effects() &&
StaticPrefs::
gfx_webrender_svg_filter_effects_also_use_for_docshell_fecolormatrix()) {
// WebRender SVGFE code needs a valid filter region, so use 1<<30 as
// rendering will already be heavily degraded at that range.
static constexpr float kExtent = 1024.0f * 1024.0f * 1024.0f;
wr::LayoutRect subregion = {{-kExtent, -kExtent}, {kExtent, kExtent}};
auto node = wr::FilterOpGraphNode{};
node.input.buffer_id = wr::FilterOpGraphPictureBufferId::None();
node.input2.buffer_id = wr::FilterOpGraphPictureBufferId::None();
node.subregion = subregion;
wrFilters.filters.AppendElement(
wr::FilterOp::SVGFESourceGraphic(node));
node.input.buffer_id = wr::FilterOpGraphPictureBufferId::BufferId(0);
wrFilters.filters.AppendElement(
wr::FilterOp::SVGFEColorMatrix(node, colorMatrix->components));
} else {
wrFilters.filters.AppendElement(
wr::FilterOp::ColorMatrix(colorMatrix->components));
}
}
wrManager->EndTransactionWithoutLayer(this, aBuilder,
@ -8284,18 +8308,25 @@ bool nsDisplayBackdropFilters::CreateWebRenderCommands(
WrFiltersHolder wrFilters;
const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
auto filterChain = style.StyleEffects()->mBackdropFilters.AsSpan();
bool initialized = true;
if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
wrFilters) &&
!SVGIntegrationUtils::BuildWebRenderFilters(
mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
initialized)) {
// Try building a CSS filter chain
WrFiltersStatus status = SVGIntegrationUtils::CreateWebRenderCSSFilters(
filterChain, mFrame, wrFilters);
if (status == WrFiltersStatus::BLOB_FALLBACK) {
// If the filters are too complex for CSS filters, try SVG filters
auto offsetForSVGFilters =
nsLayoutUtils::ComputeOffsetToUserSpace(aDisplayListBuilder, mFrame);
status = SVGIntegrationUtils::BuildWebRenderFilters(
mFrame, filterChain, StyleFilterType::BackdropFilter, wrFilters,
offsetForSVGFilters);
}
if (status == WrFiltersStatus::BLOB_FALLBACK) {
// TODO: If painting backdrop-filters on the content side is implemented,
// consider returning false to fall back to that.
wrFilters = {};
}
if (!initialized) {
if (status == WrFiltersStatus::UNSUPPORTED) {
wrFilters = {};
}
@ -8399,32 +8430,47 @@ bool nsDisplayFilters::CreateWebRenderCommands(
WrFiltersHolder wrFilters;
const ComputedStyle& style = mStyle ? *mStyle : *mFrame->Style();
auto filterChain = style.StyleEffects()->mFilters.AsSpan();
bool initialized = true;
if (!SVGIntegrationUtils::CreateWebRenderCSSFilters(filterChain, mFrame,
wrFilters) &&
!SVGIntegrationUtils::BuildWebRenderFilters(mFrame, filterChain,
StyleFilterType::Filter,
wrFilters, initialized)) {
if (mStyle) {
// Try building a CSS filter chain
WrFiltersStatus status = SVGIntegrationUtils::CreateWebRenderCSSFilters(
filterChain, mFrame, wrFilters);
if (status == WrFiltersStatus::BLOB_FALLBACK) {
// Try building an SVG filter graph
auto offsetForSVGFilters =
nsLayoutUtils::ComputeOffsetToUserSpace(aDisplayListBuilder, mFrame);
status = SVGIntegrationUtils::BuildWebRenderFilters(
mFrame, filterChain, StyleFilterType::Filter, wrFilters,
offsetForSVGFilters);
if (status == WrFiltersStatus::BLOB_FALLBACK && mStyle) {
// TODO(bug 1769223): Support fallback filters in the root code-path,
// perhaps. For now treat it the same way as invalid filters.
wrFilters = {};
} else {
// Draw using fallback.
return false;
status = WrFiltersStatus::UNSUPPORTED;
}
}
if (!initialized) {
// https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
//
// If the filter references a non-existent object or the referenced object
// is not a filter element, then the whole filter chain is ignored. No
// filter is applied to the object.
//
// Note that other engines have a weird discrepancy between SVG and HTML
// content here, but the spec is clear.
wrFilters = {};
switch (status) {
case WrFiltersStatus::BLOB_FALLBACK:
// Draw using fallback.
return false;
case WrFiltersStatus::UNSUPPORTED:
// https://drafts.fxtf.org/filter-effects/#typedef-filter-url:
//
// If the filter references a non-existent object or the referenced
// object is not a filter element, then the whole filter chain is
// ignored. No filter is applied to the object.
//
// Note that other engines have a weird discrepancy between SVG and HTML
// content here, but the spec is clear.
wrFilters = {};
break;
case WrFiltersStatus::DISABLED_FOR_PERFORMANCE:
// SVG spec allows us to drop the entire filter graph if it contains too
// many filters to render or other performance considerations.
wrFilters = {};
break;
case WrFiltersStatus::CHAIN:
case WrFiltersStatus::SVGFE:
// Filter the image using the wrFilters produced above.
break;
}
uint64_t clipChainId;

Просмотреть файл

@ -1581,12 +1581,12 @@ fuzzy(0-64,0-45) == 614272-1.svg 614272-1-ref.svg
!= 618071.html 618071-notref.html
== 619117-1.html 619117-1-ref.html
== 619511-1.html 619511-1-ref.html
fails-if(useDrawSnapshot) == 621253-1-externalFilter.html 621253-1-ref.html
fails-if(useDrawSnapshot) == 621253-1-internalFilter.html 621253-1-ref.html
fails-if(useDrawSnapshot) == 621253-2-externalFilter.html 621253-2-ref.html
fails-if(useDrawSnapshot) == 621253-2-internalFilter.html 621253-2-ref.html
fails-if(useDrawSnapshot) fuzzy-if(geckoview&&!emulator&&gfxSVGFEColorMatrix,0-4,0-32) == 621253-1-externalFilter.html 621253-1-ref.html
fails-if(useDrawSnapshot) fuzzy-if(geckoview&&!emulator&&gfxSVGFEColorMatrix,0-4,0-32) == 621253-1-internalFilter.html 621253-1-ref.html
fails-if(useDrawSnapshot) fuzzy-if(geckoview&&!emulator&&gfxSVGFEColorMatrix,0-4,0-32) == 621253-2-externalFilter.html 621253-2-ref.html
fails-if(useDrawSnapshot) fuzzy-if(geckoview&&!emulator&&gfxSVGFEColorMatrix,0-4,0-32) == 621253-2-internalFilter.html 621253-2-ref.html
random-if(winWidget) == 621918-1.svg 621918-1-ref.svg # 1-pixel diacritic positioning discrepancy in rotated text (may depend on platform fonts)
random-if(winWidget) fuzzy-if(geckoview&&!emulator,0-255,0-22) == 621918-2.svg 621918-2-ref.svg # same 1px issue as above
random-if(winWidget) fuzzy-if(geckoview&&!emulator,0-255,0-23) == 621918-2.svg 621918-2-ref.svg # same 1px issue as above
fuzzy-if(winWidget,0-5,0-1) == 622585-1.html 622585-1-ref.html # bug 789402
fuzzy(0-1,0-40000) == 625409-1.html 625409-1-ref.html
== 627393-1.html about:blank

Просмотреть файл

@ -2,7 +2,10 @@
== blend-constant-background-color.html blend-constant-background-color-ref.html
== blend-gradient-background-color.html blend-gradient-background-color-ref.html
== blend-image.html blend-image-ref.html
fuzzy-if(geckoview&&device,3-3,5-5) == blend-difference-stacking.html blend-difference-stacking-ref.html
# for some reason this test ends up with a subpixel misalignment for the '.'
# character when using gfx.webrender.svg-filters.enable=true, but other text
# works, however the text is not the purpose of this test, so just use fuzzy.
fuzzy(0-73,0-8) == blend-difference-stacking.html blend-difference-stacking-ref.html
fuzzy(0-1,0-30000) == background-blending-alpha.html background-blending-alpha-ref.html
== background-blending-gradient-color.html background-blending-gradient-color-ref.html

Просмотреть файл

@ -12,11 +12,11 @@ fuzzy-if(winWidget,47-129,47-54) == element-paint-simple.html element-paint-simp
== element-paint-recursion.html element-paint-recursion-ref.html
== element-paint-continuation.html element-paint-continuation-ref.html
== element-paint-transform-01.html element-paint-transform-01-ref.html
random-if(winWidget) fuzzy-if(!useDrawSnapshot,255-255,39-42) == element-paint-transform-02.html element-paint-transform-02-ref.html # bug 587133
fuzzy(0-201,0-1486) == element-paint-transform-02.html element-paint-transform-02-ref.html # bug 587133
== element-paint-background-size-01.html element-paint-background-size-01-ref.html
== element-paint-background-size-02.html element-paint-background-size-02-ref.html
fuzzy(0-255,0-4) == element-paint-transform-repeated.html element-paint-transform-repeated-ref.html # Bug 1475907
fuzzy-if(winWidget,0-255,0-24) fuzzy-if(!useDrawSnapshot,255-255,50-115) == element-paint-transform-03.html element-paint-transform-03-ref.html
fuzzy(0-255,0-144) == element-paint-transform-03.html element-paint-transform-03-ref.html
# For element() uses fallback / skia in WebRender, which antialiases differently from WR.
# For Windows: bug 1496542, the scrollframe snaps differently.

Просмотреть файл

@ -1,8 +1,8 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="786" viewBox="0,0,512,512">
<defs>
<clipPath id="clip-vXP8Ybe5">
<path d="M0,512v-512h512v512z"/>
</clipPath>
<path d="M0,600v-600h512v600z"/>
</clipPath>
<filter id="filter-wVmTgUOU" filterUnits="objectBoundingBox" x="-30%" y="-30%" width="160%" height="170%" color-interpolation-filters="sRGB">
<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" in="SourceGraphic"/>
<feOffset dy="28"/>

До

Ширина:  |  Высота:  |  Размер: 762 B

После

Ширина:  |  Высота:  |  Размер: 760 B

Просмотреть файл

@ -1,17 +1,17 @@
# These tests verify that CSS filters behave properly.
# e.g. filter: blur(3px)
== blur.html blur-ref.html
== blur.svg blur-ref.svg
fuzzy(0-1,0-1000000) == blur.html blur-ref.html
fuzzy(0-1,0-1000000) == blur.svg blur-ref.svg
== blur-calc.html blur-calc-ref.html
== blur-calc-negative.html blur-calc-negative-ref.html
fuzzy-if(cocoaWidget,0-1,0-2) skip-if(winWidget) == blur-cap-large-radius-on-software.html blur-cap-large-radius-on-software-ref.html
fuzzy-if(!useDrawSnapshot,2-5,4764-8168) fuzzy-if(Android&&device&&!swgl,5-5,8574-8574) == blur-clip-rect.html ../feGaussianBlur-4-ref.svg
== blur-em-radius.html blur-em-radius-ref.html
== blur-clip-rect.html ../feGaussianBlur-4-ref.svg
fuzzy(0-1,0-1000000) == blur-em-radius.html blur-em-radius-ref.html
== blur-invalid-radius.html blur-invalid-radius-ref.html
== blur-rem-radius.html blur-rem-radius-ref.html
fuzzy(0-1,0-1000000) == blur-rem-radius.html blur-rem-radius-ref.html
== blur-zero-radius.html blur-zero-radius-ref.html
fails-if(useDrawSnapshot) == blur-zoomed-page.html blur-zoomed-page-ref.html
fails-if(useDrawSnapshot) fuzzy-if(gfxSVGFEGaussianBlur,0-2,0-1000000) == blur-zoomed-page.html blur-zoomed-page-ref.html
== brightness.html brightness-ref.html
== brightness-darken.html brightness-darken-ref.html
== brightness-extreme.html brightness-extreme-ref.html
@ -25,9 +25,9 @@ fails-if(useDrawSnapshot) == blur-zoomed-page.html blur-zoomed-page-ref.html
== contrast-percent.html contrast-percent-ref.html
== contrast-reduce.html contrast-reduce-ref.html
== contrast-zero.html contrast-zero-ref.html
== drop-shadow.html drop-shadow-ref.html
== drop-shadow-default-color.html drop-shadow-default-color-ref.html
== drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
fuzzy(0-4,0-1000000) == drop-shadow.html drop-shadow-ref.html
fuzzy(0-4,0-1000000) == drop-shadow-default-color.html drop-shadow-default-color-ref.html
fuzzy(0-4,0-1000000) == drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
== filter-on-huge-bbox.html pass.svg
fuzzy(0-1,0-44) fuzzy-if(winWidget,0-1,0-198) == filter-on-outer-svg.html pass.svg
fuzzy(0-1,0-10000) == grayscale.html grayscale-ref.html

Просмотреть файл

@ -20,17 +20,17 @@ include svg-filter-chains/reftest.list
== dynamic-filter-invalidation-03.svg pass.svg
== dynamic-filter-invalidation-04.svg pass.svg
fuzzy(0-1,0-42500) == feBlend-1.svg feBlend-1-ref.svg
== feBlend-2.svg feBlend-2-ref.svg
fuzzy(0-2,0-1000000) == feBlend-1.svg feBlend-1-ref.svg
fuzzy-if(geckoview&&!emulator&&gfxSVGFEBlend,0-14,0-136) == feBlend-2.svg feBlend-2-ref.svg
fuzzy(0-1,0-6400) == feColorMatrix-1.svg feColorMatrix-1-ref.svg
fuzzy(0-1,0-10000) == feColorMatrix-2.svg feColorMatrix-2-ref.svg
fuzzy(0-2,0-1000000) == feColorMatrix-1.svg feColorMatrix-1-ref.svg
fuzzy(0-2,0-1000000) == feColorMatrix-2.svg feColorMatrix-2-ref.svg
== feComponentTransfer-1.svg feComponentTransfer-1-ref.svg
== feComponentTransfer-2.svg feComponentTransfer-2-ref.svg
fuzzy(0-1,0-9600) == feComposite-1.svg feComposite-1-ref.svg
fuzzy(0-1,0-10000) == feComposite-2.svg feComposite-2-ref.svg
fuzzy(0-2,0-1000000) == feComposite-1.svg feComposite-1-ref.svg
fuzzy(0-2,0-1000000) == feComposite-2.svg feComposite-2-ref.svg
== feConvolveMatrix-1.svg feConvolveMatrix-1-ref.svg
== feConvolveMatrix-2.svg feConvolveMatrix-2-ref.svg
@ -38,16 +38,24 @@ fuzzy(0-1,0-10000) == feComposite-2.svg feComposite-2-ref.svg
== feDisplacementMap-1.svg feDisplacementMap-1-ref.svg
== feDisplacementMap-2.svg feDisplacementMap-2-ref.svg
fuzzy(0-1,0-1600) == feFlood-1.svg feFlood-1-ref.svg
skip-if(winWidget) fuzzy(0-1,0-6400) == feFlood-2.svg feFlood-2-ref.svg
fuzzy(0-2,0-1000000) == feFlood-1.svg feFlood-1-ref.svg
fuzzy(0-2,0-1000000) == feFlood-2.svg feFlood-2-ref.svg
fuzzy(0-2,0-6404) fuzzy-if(Android&&device&&!swgl,6-6,6400-6400) == feGaussianBlur-1.svg feGaussianBlur-1-ref.svg
fuzzy(0-2,0-304) == feGaussianBlur-2.svg feGaussianBlur-2-ref.svg
# NOTE: some of these use multiple fuzzy conditions, each fuzzy condition
# is simply setting the fuzzy parameters that will be used by the == at the end,
# so the last fuzzy wins in terms of override order.
fuzzy(0-8,0-1000000) == feGaussianBlur-1.svg feGaussianBlur-1-ref.svg
pref(gfx.webrender.svg-filter-effects,false) fuzzy(0-6,0-1000000) == feGaussianBlur-2.svg feGaussianBlur-2-ref.svg
pref(gfx.webrender.svg-filter-effects,true) fuzzy(0-8,0-1000000) == feGaussianBlur-2.svg feGaussianBlur-2-ref.svg
# != feGaussianBlur-3.svg feGaussianBlur-3-ref.svg
fuzzy-if(!useDrawSnapshot,2-5,4764-8168) fuzzy-if(Android&&device&&!swgl,5-5,8574-8574) == feGaussianBlur-4.svg feGaussianBlur-4-ref.svg
fuzzy-if(geckoview,0-4,0-200) == feGaussianBlur-5.svg feGaussianBlur-5-ref.svg
== feGaussianBlur-6.svg feGaussianBlur-6-ref.svg
skip-if(winWidget) == feGaussianBlur-cap-large-directional-radius-on-software.html feGaussianBlur-cap-large-directional-radius-on-software-ref.html
pref(gfx.webrender.svg-filter-effects,false) == feGaussianBlur-4.svg feGaussianBlur-4-ref.svg
pref(gfx.webrender.svg-filter-effects,true) == feGaussianBlur-4.svg feGaussianBlur-4-ref.svg
fuzzy-if(geckoview,0-4,0-200) fuzzy-if(gfxSVGFEGaussianBlur,0-0,0-0) == feGaussianBlur-5.svg feGaussianBlur-5-ref.svg
fuzzy(0-2,0-10000) == feGaussianBlur-6.svg feGaussianBlur-6-ref.svg
# SVGFE implementation deliberately does not cap blur radius (but resolution
# degrades), the regular WebRender CSS filter also does not need to cap radius
# for the same reason, so this test is now only relevant to blob fallback
skip-if(!useDrawSnapshot) == feGaussianBlur-cap-large-directional-radius-on-software.html feGaussianBlur-cap-large-directional-radius-on-software-ref.html
!= feImage-1.svg about:blank # (Make sure our image renders at all)
== feImage-1.svg feImage-1-ref.svg
@ -67,10 +75,10 @@ skip-if(winWidget) == feGaussianBlur-cap-large-directional-radius-on-software.ht
# no tests for feTurbulence
fuzzy-if(geckoview,0-36,0-220) == filter-clipped-rect-01.svg pass.svg
== filter-clipped-rect-01.svg pass.svg
== filter-in-pattern-01.svg pass.svg
fuzzy(0-5,0-67) != filter-in-pattern-02.svg filter-in-pattern-02-ref.svg
random-if(winWidget) == filter-in-mask-01.svg pass.svg # bug 1356139
== filter-in-mask-01.svg pass.svg
== filter-in-mask-02.svg pass.svg
== filter-inner-svg-01.svg pass.svg
== filter-inner-svg-02.svg pass.svg
@ -99,7 +107,7 @@ fuzzy(0-10,0-1200) == filter-nested-filtering-02.svg pass.svg
== feComponentTransfer-04.svg pass.svg
== feComposite-arguments-01.svg pass.svg
fuzzy-if(winWidget,0-1,0-39600) == feComposite-operator-lighter.svg feComposite-operator-lighter-ref.html
fuzzy(0-85,0-28600) == feComposite-paint-01.svg feComposite-paint-01-ref.svg
fuzzy(0-85,0-28600) fuzzy-if(gfxSVGFEComposite,0-2,0-1000000) == feComposite-paint-01.svg feComposite-paint-01-ref.svg
fuzzy(0-1,0-10000) == feConvolveMatrix-bias-01.svg feConvolveMatrix-bias-01-ref.svg
== feConvolveMatrix-order-01.svg feConvolveMatrix-order-01-ref.svg
@ -107,11 +115,11 @@ fuzzy(0-1,0-400) == feDisplacementMap-alpha-01.svg pass.svg
fuzzy(0-2,0-500) == feDisplacementMap-colour-01.svg feDisplacementMap-colour-01-ref.svg
== feDisplacementMap-scale-01.svg pass.svg
fuzzy-if(winWidget,0-2,0-25) fuzzy-if(!useDrawSnapshot,55-98,14033-16467) == feDropShadow-01.svg feDropShadow-01-ref.svg
fuzzy-if(winWidget,0-2,0-25) fuzzy-if(!useDrawSnapshot,55-98,14033-16467) fuzzy-if(gfxSVGFEDropShadow&&!gfxSVGFEOffset,97-98,21575-22951) fuzzy-if(gfxSVGFEOffset,0-2,0-1000000) == feDropShadow-01.svg feDropShadow-01-ref.svg
== feFlood-color-01.svg pass.svg
fuzzy-if(!useDrawSnapshot||(winWidget&&isCoverageBuild),20-21,5523-5647) == feGaussianBlur-alpha-01.svg feGaussianBlur-alpha-01-ref.svg
fuzzy(0-21,0-1000000) == feGaussianBlur-alpha-01.svg feGaussianBlur-alpha-01-ref.svg
== feMorphology-radius-negative-01.svg pass.svg
== feMorphology-radius-negative-02.svg pass.svg
@ -135,8 +143,7 @@ fuzzy(0-1,0-10000) == feTurbulence-zero-baseFreq-01.svg feTurbulence-zero-baseFr
!= feTurbulence-zero-baseFreq-02.svg about:blank
== outside-sourcegraphic-1.svg outside-sourcegraphic-ref.svg
# These failures are caused by bug 1586055
fails-if(!useDrawSnapshot) == outside-sourcegraphic-2.svg outside-sourcegraphic-ref.svg
fails-if(!useDrawSnapshot) == outside-sourcegraphic-3.svg outside-sourcegraphic-ref.svg
== nested-filter.html nested-filter-ref.html
== outside-sourcegraphic-2.svg outside-sourcegraphic-ref.svg
== outside-sourcegraphic-3.svg outside-sourcegraphic-ref.svg
fuzzy-if(geckoview&&!emulator,0-4,0-124) == nested-filter.html nested-filter-ref.html
== filter-giant.svg pass.svg

Просмотреть файл

@ -4,13 +4,13 @@
== clip-input.svg clip-input-ref.svg
== clip-original-SourceGraphic.svg clip-original-SourceGraphic-ref.svg
== clip-output.svg clip-output-ref.svg
fuzzy(0-5,0-20300) fuzzy-if(Android&&device&&!swgl,5-5,21751-21751) == default-subregion.svg default-subregion-ref.svg
fuzzy(0-1,0-1000000) == default-subregion.svg default-subregion-ref.svg
== different-FillPaint-filter-regions.svg different-FillPaint-filter-regions-ref.svg
== different-StrokePaint-filter-regions.svg different-StrokePaint-filter-regions-ref.svg
== dont-clip-previous-primitives.svg dont-clip-previous-primitives-ref.svg
== intersecting-filter-regions.svg intersecting-filter-regions-ref.svg
fuzzy-if(!useDrawSnapshot,9-9,5168-5536) fuzzy-if(!useDrawSnapshot&&swgl,7-7,13170-13184) fuzzy-if(Android&&device&&!swgl,8-8,12391-12391) == long-chain.svg simple-chain-ref.svg
fuzzy-if(!useDrawSnapshot,9-9,5168-5536) fuzzy-if(!useDrawSnapshot&&swgl,7-7,13170-13184) fuzzy-if(Android&&device&&!swgl,8-8,12391-12391) == multiple-primitives-per-filter.svg simple-chain-ref.svg
fuzzy-if(winWidget,0-1,0-173) fuzzy-if(!useDrawSnapshot||(winWidget&&isCoverageBuild),9-9,5128-5496) fuzzy-if(!useDrawSnapshot&&swgl,7-7,12820-12830) fuzzy-if(Android&&device&&!swgl,8-8,12355-12355) == second-filter-uses-SourceAlpha.svg second-filter-uses-SourceAlpha-ref.svg
fuzzy-if(!useDrawSnapshot,9-9,5168-5536) fuzzy-if(!useDrawSnapshot&&swgl,7-7,13170-13180) fuzzy-if(Android&&device&&!swgl,8-8,12391-12391) == second-filter-uses-SourceGraphic.svg simple-chain-ref.svg
== long-chain.svg simple-chain-ref.svg
== multiple-primitives-per-filter.svg simple-chain-ref.svg
fuzzy(0-1,0-1000000) == second-filter-uses-SourceAlpha.svg second-filter-uses-SourceAlpha-ref.svg
== second-filter-uses-SourceGraphic.svg simple-chain-ref.svg
== simple-chain.svg simple-chain-ref.svg

Просмотреть файл

@ -61,7 +61,10 @@ skip == blend-difference-stacking.html blend-difference-stacking-ref.html # bug
== active-transform-blend-mode.html active-transform-blend-mode-ref.html
== active-clip-and-mask.html active-clip-and-mask-ref.html
fuzzy(0-11,0-7155) == blur-inside-clipPath.svg blur-inside-clipPath-ref.svg
# this test exists to detect if clip paths are erroneously cropped by the
# unfiltered size of the primitive, which was bug 1459890, so the blur fuzz
# doesn't matter, as if the error occurs it causes a much more drastic change
fuzzy(0-20,0-9840) == blur-inside-clipPath.svg blur-inside-clipPath-ref.svg
== border-radius-01.html pass.svg
== mask-image-filter-transform.html mask-image-filter-transform-ref.html
@ -177,7 +180,7 @@ fuzzy-if(winWidget,0-3,0-1200) == dynamic-rect-02.svg dynamic-rect-02-ref.svg #
== dynamic-stroke-width-01.svg pass.svg
== dynamic-switch-01.svg pass.svg
== dynamic-text-01.svg dynamic-text-01-ref.svg
fuzzy-if(winWidget,0-3,0-12739) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7, Win8
fuzzy-if(winWidget,0-4,0-12739) fuzzy-if(gfxSVGFEGaussianBlur,0-7,0-1000000) == dynamic-text-02.svg dynamic-text-02-ref.svg # bug 776038 for Win7, Win8
fuzzy-if(winWidget,0-2,0-10539) == dynamic-text-03.svg dynamic-text-03-ref.svg # bug 776038 for Win7
== dynamic-text-04.svg dynamic-text-04-ref.svg
== dynamic-text-05.svg pass.svg
@ -471,7 +474,7 @@ fuzzy(0-1,0-2600) == svg-in-foreignObject-02.xhtml svg-in-foreignObject-01-ref.x
== suspend-08.svg pass.svg
== svg-effects-area-unzoomed.xhtml svg-effects-area-unzoomed-ref.xhtml
== svg-effects-area-zoomed-in.xhtml svg-effects-area-zoomed-in-ref.xhtml
fuzzy-if(geckoview&&!emulator&&gfxSVGFEComponentTransfer,0-4,0-305) == svg-effects-area-zoomed-in.xhtml svg-effects-area-zoomed-in-ref.xhtml
== svg-effects-area-zoomed-out.xhtml svg-effects-area-zoomed-out-ref.xhtml
== svg-transform-01.svg pass.svg
== svg-transform-02.svg pass.svg

Просмотреть файл

@ -18,7 +18,7 @@ fuzzy-if(Android,0-255,0-30) == clipPath-html-06-extref.xhtml clipPath-html-06-r
fails-if(useDrawSnapshot) == clipPath-html-zoomed-01.xhtml clipPath-html-01-ref.svg
== clipPath-transformed-html-01.xhtml ../pass.svg
== clipPath-transformed-html-02.xhtml ../pass.svg
== css-and-svg-filter-01.html css-and-svg-filter-01-ref.html
fuzzy-if(geckoview&&!emulator,0-3,0-20) == css-and-svg-filter-01.html css-and-svg-filter-01-ref.html
== conditions-outer-svg-01.xhtml ../pass.svg
== conditions-outer-svg-02.xhtml ../pass.svg
== dynamic-conditions-outer-svg-01.xhtml ../pass.svg

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -131,15 +131,28 @@ class FilterInstance {
/**
* Try to build WebRender filters for a frame if the filters applied to it are
* supported. aInitialized is set to true if the filter has been initialized
* and false otherwise (e.g. a bad url). If aInitialized is false the filter
* the filter contents should not be drawn.
* supported, returns a status that indicates which code path will handle the
* filters on this frame, or if we must fall back to blob image.
*/
static bool BuildWebRenderFilters(
static WrFiltersStatus BuildWebRenderFilters(
nsIFrame* aFilteredFrame,
mozilla::Span<const mozilla::StyleFilter> aFilters,
StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
bool& aInitialized);
const nsPoint& aOffsetForSVGFilters);
/**
* Try to build WebRender SVG filter graph for a frame based on SVG and CSS
* filters. If given an unreasonably costly set of filters this can reject
* the entire filter graph (a behavior permitted by SVG spec).
*
* See WrFiltersStatus for possible outcomes.
* Prefs such as gfx.webrender.svg-filter-effects affect this.
*/
static WrFiltersStatus BuildWebRenderSVGFiltersImpl(
nsIFrame* aFilteredFrame,
mozilla::Span<const mozilla::StyleFilter> aFilters,
StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
const nsPoint& aOffsetForSVGFilters);
private:
/**
@ -166,6 +179,8 @@ class FilterInstance {
* ink overflow rect for the target element.
* @param aOverrideBBox [optional] Use a different SVG bbox for the target
* element. Must be non-null if aTargetFrame is null.
* @param aFilterSpaceBoundsNotSnapped [optional] The calculated bbox in
* userspace can be returend in the provided outparam.
*/
FilterInstance(
nsIFrame* aTargetFrame, nsIContent* aTargetContent,
@ -177,13 +192,13 @@ class FilterInstance {
const nsRegion* aPostFilterDirtyRegion = nullptr,
const nsRegion* aPreFilterDirtyRegion = nullptr,
const nsRect* aPreFilterInkOverflowRectOverride = nullptr,
const gfxRect* aOverrideBBox = nullptr);
const gfxRect* aOverrideBBox = nullptr,
gfxRect* aFilterSpaceBoundsNotSnapped = nullptr);
static bool BuildWebRenderFiltersImpl(
static WrFiltersStatus BuildWebRenderFiltersImpl(
nsIFrame* aFilteredFrame,
mozilla::Span<const mozilla::StyleFilter> aFilters,
StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
bool& aInitialized);
StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters);
/**
* Returns true if the filter instance was created successfully.
@ -366,6 +381,11 @@ class FilterInstance {
*/
nsIntRect mTargetBBoxInFilterSpace;
/**
* The SVG filter element rect, in filter space, may be non-integer.
*/
gfxRect mFilterSpaceBoundsNotSnapped;
/**
* Transform rects between filter space and frame space in CSS pixels.
*/

Просмотреть файл

@ -32,7 +32,8 @@ SVGFilterInstance::SVGFilterInstance(
const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame,
nsIContent* aTargetContent, const UserSpaceMetrics& aMetrics,
const gfxRect& aTargetBBox,
const MatrixScalesDouble& aUserSpaceToFilterSpaceScale)
const MatrixScalesDouble& aUserSpaceToFilterSpaceScale,
gfxRect& aFilterSpaceBoundsNotSnapped)
: mFilter(aFilter),
mTargetContent(aTargetContent),
mMetrics(aMetrics),
@ -54,6 +55,7 @@ SVGFilterInstance::SVGFilterInstance(
if (!ComputeBounds()) {
return;
}
aFilterSpaceBoundsNotSnapped = mFilterSpaceBoundsNotSnapped;
mInitialized = true;
}
@ -88,6 +90,7 @@ bool SVGFilterInstance::ComputeBounds() {
// can align them with the pixel boundaries of the offscreen surface.
// The offscreen surface has the same scale as filter space.
gfxRect filterSpaceBounds = UserSpaceToFilterSpace(userSpaceBounds);
mFilterSpaceBoundsNotSnapped = filterSpaceBounds;
filterSpaceBounds.RoundOut();
if (filterSpaceBounds.width <= 0 || filterSpaceBounds.height <= 0) {
// 0 disables rendering, < 0 is error. dispatch error console warning

Просмотреть файл

@ -79,11 +79,12 @@ class SVGFilterInstance {
* @param aTargetBBox The SVG bbox to use for the target frame, computed by
* the caller. The caller may decide to override the actual SVG bbox.
*/
SVGFilterInstance(
const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame,
nsIContent* aTargetContent, const UserSpaceMetrics& aMetrics,
const gfxRect& aTargetBBox,
const gfx::MatrixScalesDouble& aUserSpaceToFilterSpaceScale);
SVGFilterInstance(const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame,
nsIContent* aTargetContent,
const UserSpaceMetrics& aMetrics,
const gfxRect& aTargetBBox,
const gfx::MatrixScalesDouble& aUserSpaceToFilterSpaceScale,
gfxRect& aFilterSpaceBoundsNotSnapped);
/**
* Returns true if the filter instance was created successfully.
@ -225,6 +226,11 @@ class SVGFilterInstance {
*/
nsIntRect mFilterSpaceBounds;
/**
* The bounds of the filter element itself, which may be non-integer.
*/
gfxRect mFilterSpaceBoundsNotSnapped;
/**
* The scale factors between user space and filter space.
*/

Просмотреть файл

@ -949,9 +949,17 @@ void SVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams,
opacity);
}
bool SVGIntegrationUtils::CreateWebRenderCSSFilters(
WrFiltersStatus SVGIntegrationUtils::CreateWebRenderCSSFilters(
Span<const StyleFilter> aFilters, nsIFrame* aFrame,
WrFiltersHolder& aWrFilters) {
// Check if prefs are set to convert the CSS filters to SVG filters and use
// the new WebRender SVG filter rendering, rather than the existing CSS filter
// support
if (StaticPrefs::gfx_webrender_svg_filter_effects() &&
StaticPrefs::
gfx_webrender_svg_filter_effects_also_convert_css_filters()) {
return WrFiltersStatus::BLOB_FALLBACK;
}
// All CSS filters are supported by WebRender. SVG filters are not fully
// supported, those use NS_STYLE_FILTER_URL and are handled separately.
@ -959,8 +967,10 @@ bool SVGIntegrationUtils::CreateWebRenderCSSFilters(
// succeeded, and don't render any of them.
if (aFilters.Length() >
StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) {
return true;
return WrFiltersStatus::DISABLED_FOR_PERFORMANCE;
}
// Track status so we can do cleanup if unsupported filters are found.
WrFiltersStatus status = WrFiltersStatus::CHAIN;
aWrFilters.filters.SetCapacity(aFilters.Length());
auto& wrFilters = aWrFilters.filters;
for (const StyleFilter& filter : aFilters) {
@ -1024,28 +1034,39 @@ bool SVGIntegrationUtils::CreateWebRenderCSSFilters(
break;
}
default:
return false;
status = WrFiltersStatus::BLOB_FALLBACK;
break;
}
if (status != WrFiltersStatus::CHAIN) {
break;
}
}
return true;
if (status != WrFiltersStatus::CHAIN) {
// Clean up the filters holder if we can't render filters this way.
aWrFilters = {};
}
return status;
}
bool SVGIntegrationUtils::BuildWebRenderFilters(
WrFiltersStatus SVGIntegrationUtils::BuildWebRenderFilters(
nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters,
StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
bool& aInitialized) {
return FilterInstance::BuildWebRenderFilters(
aFilteredFrame, aFilters, aStyleFilterType, aWrFilters, aInitialized);
const nsPoint& aOffsetForSVGFilters) {
return FilterInstance::BuildWebRenderFilters(aFilteredFrame, aFilters,
aStyleFilterType, aWrFilters,
aOffsetForSVGFilters);
}
bool SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(nsIFrame* aFrame) {
WrFiltersHolder wrFilters;
auto filterChain = aFrame->StyleEffects()->mFilters.AsSpan();
bool initialized = true;
return CreateWebRenderCSSFilters(filterChain, aFrame, wrFilters) ||
BuildWebRenderFilters(aFrame, filterChain, StyleFilterType::Filter,
wrFilters, initialized);
WrFiltersStatus status =
CreateWebRenderCSSFilters(filterChain, aFrame, wrFilters);
if (status == WrFiltersStatus::BLOB_FALLBACK) {
status = BuildWebRenderFilters(aFrame, filterChain, StyleFilterType::Filter,
wrFilters, nsPoint());
}
return status == WrFiltersStatus::CHAIN || status == WrFiltersStatus::SVGFE;
}
bool SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(

Просмотреть файл

@ -22,7 +22,27 @@ struct nsPoint;
struct nsRect;
struct nsSize;
enum class WrFiltersStatus {
// Image will be rendered unftilered - the filter graph contains invalid refs
// (which SVG spec states will be rendered as if there is no filter graph).
UNSUPPORTED = 0,
// Image will be rendered unfiltered - the filter graph provided is
// excessively costly to render and has been dropped (per SVG spec we can do
// this to preserve user experience).
DISABLED_FOR_PERFORMANCE = 1,
// Image will be rendered using blob fallback (software rendering) due to
// unsupported operations (in either the CSS or SVGFE path).
BLOB_FALLBACK = 2,
// Image will be rendered using a simple CSS filter chain in WebRender.
CHAIN = 3,
// Filter graph will be rendered using WebRender SVGFE code path, this can
// handle any kind of filter graph consisting of supported operations (most
// operations are supported).
SVGFE = 4,
};
struct WrFiltersHolder {
// TODO(Bug 1899691): Better to use AutoTArray here...
nsTArray<mozilla::wr::FilterOp> filters;
nsTArray<mozilla::wr::WrFilterData> filter_datas;
mozilla::Maybe<nsRect> post_filters_clip;
@ -192,19 +212,18 @@ class SVGIntegrationUtils final {
/**
* Build WebRender filters for a frame with CSS filters applied to it.
*/
static bool CreateWebRenderCSSFilters(Span<const StyleFilter> aFilters,
nsIFrame* aFrame,
WrFiltersHolder& aWrFilters);
static WrFiltersStatus CreateWebRenderCSSFilters(
Span<const StyleFilter> aFilters, nsIFrame* aFrame,
WrFiltersHolder& aWrFilters);
/**
* Try to build WebRender filters for a frame with SVG filters applied to it
* if the filters are supported.
*/
static bool BuildWebRenderFilters(nsIFrame* aFilteredFrame,
Span<const StyleFilter> aFilters,
StyleFilterType aStyleFilterType,
WrFiltersHolder& aWrFilters,
bool& aInitialized);
static WrFiltersStatus BuildWebRenderFilters(
nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters,
StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters,
const nsPoint& aOffsetForSVGFilters);
/**
* Check if the filters present on |aFrame| are supported by WebRender.

Просмотреть файл

@ -648,6 +648,36 @@ function BuildConditionSandbox(aURL) {
sandbox.swgl = g.windowUtils.layerManagerType.startsWith(
"WebRender (Software"
);
// These detect if each SVG filter primitive is enabled in WebRender
sandbox.gfxSVGFE =
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects") &&
!g.useDrawSnapshot;
sandbox.gfxSVGFEColorMatrix =
Services.prefs.getBoolPref(
"gfx.webrender.svg-filter-effects.fecolormatrix"
) && sandbox.gfxSVGFE;
sandbox.gfxSVGFEComponentTransfer =
Services.prefs.getBoolPref(
"gfx.webrender.svg-filter-effects.fecomponenttransfer"
) && sandbox.gfxSVGFE;
sandbox.gfxSVGFEComposite =
Services.prefs.getBoolPref(
"gfx.webrender.svg-filter-effects.fecomposite"
) && sandbox.gfxSVGFE;
sandbox.gfxSVGFEDropShadow =
Services.prefs.getBoolPref(
"gfx.webrender.svg-filter-effects.fedropshadow"
) && sandbox.gfxSVGFE;
sandbox.gfxSVGFEFlood =
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feflood") &&
sandbox.gfxSVGFE;
sandbox.gfxSVGFEGaussianBlur =
Services.prefs.getBoolPref(
"gfx.webrender.svg-filter-effects.fegaussianblur"
) && sandbox.gfxSVGFE;
sandbox.gfxSVGFEOffset =
Services.prefs.getBoolPref("gfx.webrender.svg-filter-effects.feoffset") &&
sandbox.gfxSVGFE;
// Use this to annotate reftests that fail in drawSnapshot, but
// the reason hasn't been investigated (or fixed) yet.

Просмотреть файл

@ -6525,6 +6525,117 @@
mirror: once
#endif
- name: gfx.webrender.svg-filter-effects
type: RelaxedAtomicBool
value: @IS_NIGHTLY_BUILD@
mirror: always
- name: gfx.webrender.svg-filter-effects.also-convert-css-filters
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.also-use-for-docshell-fecolormatrix
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.opacity
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.toalpha
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.feblend
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.fecolormatrix
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.fecomponenttransfer
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.fecomposite
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.feconvolvematrix
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.fediffuselighting
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.fedisplacementmap
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.fedropshadow
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.feflood
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.fegaussianblur
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.feimage
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.femerge
type: RelaxedAtomicBool
value: true
mirror: always
- name: gfx.webrender.svg-filter-effects.femorphology
type: RelaxedAtomicBool
value: false
mirror: always
# Disabled for now due to https://bugzilla.mozilla.org/show_bug.cgi?id=1902136
- name: gfx.webrender.svg-filter-effects.feoffset
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.fespecularlighting
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.fetile
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-filter-effects.feturbulence
type: RelaxedAtomicBool
value: false
mirror: always
- name: gfx.webrender.svg-images
type: RelaxedAtomicBool
value: true

Просмотреть файл

@ -0,0 +1,3 @@
[blur-text.html]
fuzzy:
maxDifference=0-8;totalPixels=0-55000

Просмотреть файл

@ -1,3 +0,0 @@
[effect-reference-on-transparent-element.html]
expected:
FAIL

Просмотреть файл

@ -1,3 +0,0 @@
[filter-region-transformed-child-001.html]
expected:
FAIL

Просмотреть файл

@ -1,2 +1,4 @@
[svg-multiple-filter-functions.html]
expected: FAIL
fuzzy:
maxDifference=0-1;totalPixels=0-10000