diff options
Diffstat (limited to 'gfx/wr/webrender/src/render_target.rs')
-rw-r--r-- | gfx/wr/webrender/src/render_target.rs | 196 |
1 files changed, 190 insertions, 6 deletions
diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs index f53b5dd4f8..c50a1b2303 100644 --- a/gfx/wr/webrender/src/render_target.rs +++ b/gfx/wr/webrender/src/render_target.rs @@ -14,10 +14,10 @@ use crate::spatial_tree::SpatialTree; use crate::clip::{ClipStore, ClipItemKind}; use crate::frame_builder::{FrameGlobalResources}; use crate::gpu_cache::{GpuCache, GpuCacheAddress}; -use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; +use crate::gpu_types::{BorderInstance, SvgFilterInstance, SVGFEFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; use crate::gpu_types::{TransformPalette, ZBufferIdGenerator, MaskInstance, ClipSpace}; use crate::gpu_types::{ZBufferId, QuadSegment, PrimitiveInstanceData, TransformPaletteId}; -use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId}; +use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId, FilterGraphOp}; use crate::picture::{SliceId, SurfaceInfo, ResolvedSurfaceTexture, TileCacheInstance}; use crate::quad; use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PrimitiveScratchBuffer}; @@ -28,7 +28,7 @@ use crate::prim_store::gradient::{ use crate::renderer::{GpuBufferAddress, GpuBufferBuilder}; use crate::render_backend::DataStores; use crate::render_task::{RenderTaskKind, RenderTaskAddress, SubPass}; -use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo, MaskSubPass}; +use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo, MaskSubPass, SVGFEFilterTask}; use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; use crate::resource_cache::ResourceCache; use crate::spatial_tree::{SpatialNodeIndex}; @@ -226,6 +226,7 @@ pub struct ColorRenderTarget { pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>, pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>, pub svg_filters: Vec<(BatchTextures, Vec<SvgFilterInstance>)>, + pub svg_nodes: Vec<(BatchTextures, Vec<SVGFEFilterInstance>)>, pub blits: Vec<BlitJob>, alpha_tasks: Vec<RenderTaskId>, screen_size: DeviceIntSize, @@ -256,6 +257,7 @@ impl RenderTarget for ColorRenderTarget { horizontal_blurs: FastHashMap::default(), scalings: FastHashMap::default(), svg_filters: Vec::new(), + svg_nodes: Vec::new(), blits: Vec::new(), alpha_tasks: Vec::new(), screen_size, @@ -263,7 +265,7 @@ impl RenderTarget for ColorRenderTarget { used_rect, resolve_ops: Vec::new(), clear_color: Some(ColorF::TRANSPARENT), - prim_instances: [Vec::new(), Vec::new()], + prim_instances: [Vec::new(), Vec::new(), Vec::new(), Vec::new()], prim_instances_with_scissor: FastHashMap::default(), clip_masks: ClipMaskInstanceList::new(), } @@ -438,6 +440,17 @@ impl RenderTarget for ColorRenderTarget { task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)), ) } + RenderTaskKind::SVGFENode(ref task_info) => { + add_svg_filter_node_instances( + &mut self.svg_nodes, + render_tasks, + &task_info, + task, + task.children.get(0).cloned(), + task.children.get(1).cloned(), + task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)), + ) + } RenderTaskKind::Image(..) | RenderTaskKind::Cached(..) | RenderTaskKind::ClipRegion(..) | @@ -559,7 +572,8 @@ impl RenderTarget for AlphaRenderTarget { RenderTaskKind::ConicGradient(..) | RenderTaskKind::TileComposite(..) | RenderTaskKind::Prim(..) | - RenderTaskKind::SvgFilter(..) => { + RenderTaskKind::SvgFilter(..) | + RenderTaskKind::SVGFENode(..) => { panic!("BUG: should not be added to alpha target!"); } RenderTaskKind::Empty(..) => { @@ -799,7 +813,8 @@ impl TextureCacheRenderTarget { RenderTaskKind::Scaling(..) | RenderTaskKind::TileComposite(..) | RenderTaskKind::Empty(..) | - RenderTaskKind::SvgFilter(..) => { + RenderTaskKind::SvgFilter(..) | + RenderTaskKind::SVGFENode(..) => { panic!("BUG: unexpected task kind for texture cache target"); } #[cfg(test)] @@ -945,6 +960,175 @@ fn add_svg_filter_instances( instances.push((textures, vec![instance])); } +/// Generates SVGFEFilterInstances from a single SVGFEFilterTask, this is what +/// prepares vertex data for the shader, and adds it to the appropriate batch. +/// +/// The interesting parts of the handling of SVG filters are: +/// * scene_building.rs : wrap_prim_with_filters +/// * picture.rs : get_coverage_svgfe +/// * render_task.rs : new_svg_filter_graph +/// * render_target.rs : add_svg_filter_node_instances (you are here) +fn add_svg_filter_node_instances( + instances: &mut Vec<(BatchTextures, Vec<SVGFEFilterInstance>)>, + render_tasks: &RenderTaskGraph, + task_info: &SVGFEFilterTask, + target_task: &RenderTask, + input_1_task: Option<RenderTaskId>, + input_2_task: Option<RenderTaskId>, + extra_data_address: Option<GpuCacheAddress>, +) { + let node = &task_info.node; + let op = &task_info.op; + let mut textures = BatchTextures::empty(); + + // We have to undo the inflate here as the inflated target rect is meant to + // have a blank border + let target_rect = target_task + .get_target_rect() + .inner_box(DeviceIntSideOffsets::new(node.inflate as i32, node.inflate as i32, node.inflate as i32, node.inflate as i32)) + .to_f32(); + + let mut instance = SVGFEFilterInstance { + target_rect, + input_1_content_scale_and_offset: [0.0; 4], + input_2_content_scale_and_offset: [0.0; 4], + input_1_task_address: RenderTaskId::INVALID.into(), + input_2_task_address: RenderTaskId::INVALID.into(), + kind: 0, + input_count: node.inputs.len() as u16, + extra_data_address: extra_data_address.unwrap_or(GpuCacheAddress::INVALID), + }; + + // Must match FILTER_* in cs_svg_filter_node.glsl + instance.kind = match op { + // Identity does not modify color, no linear case + FilterGraphOp::SVGFEIdentity => 0, + // SourceGraphic does not have its own shader mode, it uses Identity. + FilterGraphOp::SVGFESourceGraphic => 0, + // SourceAlpha does not have its own shader mode, it uses ToAlpha. + FilterGraphOp::SVGFESourceAlpha => 4, + // Opacity scales the entire rgba color, so it does not need a linear + // case as the rgb / a ratio does not change (sRGB is a curve on the RGB + // before alpha multiply, not after) + FilterGraphOp::SVGFEOpacity{..} => 2, + FilterGraphOp::SVGFEToAlpha => 4, + FilterGraphOp::SVGFEBlendColor => {match node.linear {false => 6, true => 7}}, + FilterGraphOp::SVGFEBlendColorBurn => {match node.linear {false => 8, true => 9}}, + FilterGraphOp::SVGFEBlendColorDodge => {match node.linear {false => 10, true => 11}}, + FilterGraphOp::SVGFEBlendDarken => {match node.linear {false => 12, true => 13}}, + FilterGraphOp::SVGFEBlendDifference => {match node.linear {false => 14, true => 15}}, + FilterGraphOp::SVGFEBlendExclusion => {match node.linear {false => 16, true => 17}}, + FilterGraphOp::SVGFEBlendHardLight => {match node.linear {false => 18, true => 19}}, + FilterGraphOp::SVGFEBlendHue => {match node.linear {false => 20, true => 21}}, + FilterGraphOp::SVGFEBlendLighten => {match node.linear {false => 22, true => 23}}, + FilterGraphOp::SVGFEBlendLuminosity => {match node.linear {false => 24, true => 25}}, + FilterGraphOp::SVGFEBlendMultiply => {match node.linear {false => 26, true => 27}}, + FilterGraphOp::SVGFEBlendNormal => {match node.linear {false => 28, true => 29}}, + FilterGraphOp::SVGFEBlendOverlay => {match node.linear {false => 30, true => 31}}, + FilterGraphOp::SVGFEBlendSaturation => {match node.linear {false => 32, true => 33}}, + FilterGraphOp::SVGFEBlendScreen => {match node.linear {false => 34, true => 35}}, + FilterGraphOp::SVGFEBlendSoftLight => {match node.linear {false => 36, true => 37}}, + FilterGraphOp::SVGFEColorMatrix{..} => {match node.linear {false => 38, true => 39}}, + FilterGraphOp::SVGFEComponentTransfer => unreachable!(), + FilterGraphOp::SVGFEComponentTransferInterned{..} => {match node.linear {false => 40, true => 41}}, + FilterGraphOp::SVGFECompositeArithmetic{..} => {match node.linear {false => 42, true => 43}}, + FilterGraphOp::SVGFECompositeATop => {match node.linear {false => 44, true => 45}}, + FilterGraphOp::SVGFECompositeIn => {match node.linear {false => 46, true => 47}}, + FilterGraphOp::SVGFECompositeLighter => {match node.linear {false => 48, true => 49}}, + FilterGraphOp::SVGFECompositeOut => {match node.linear {false => 50, true => 51}}, + FilterGraphOp::SVGFECompositeOver => {match node.linear {false => 52, true => 53}}, + FilterGraphOp::SVGFECompositeXOR => {match node.linear {false => 54, true => 55}}, + FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {match node.linear {false => 56, true => 57}}, + FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {match node.linear {false => 58, true => 59}}, + FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {match node.linear {false => 60, true => 61}}, + FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {match node.linear {false => 62, true => 63}}, + FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {match node.linear {false => 64, true => 65}}, + FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {match node.linear {false => 66, true => 67}}, + FilterGraphOp::SVGFEDisplacementMap{..} => {match node.linear {false => 68, true => 69}}, + FilterGraphOp::SVGFEDropShadow{..} => {match node.linear {false => 70, true => 71}}, + // feFlood takes an sRGB color and does no math on it, no linear case + FilterGraphOp::SVGFEFlood{..} => 72, + FilterGraphOp::SVGFEGaussianBlur{..} => {match node.linear {false => 74, true => 75}}, + // feImage does not meaningfully modify the color of its input, though a + // case could be made for gamma-correct image scaling, that's a bit out + // of scope for now + FilterGraphOp::SVGFEImage{..} => 76, + FilterGraphOp::SVGFEMorphologyDilate{..} => {match node.linear {false => 80, true => 81}}, + FilterGraphOp::SVGFEMorphologyErode{..} => {match node.linear {false => 82, true => 83}}, + FilterGraphOp::SVGFESpecularLightingDistant{..} => {match node.linear {false => 86, true => 87}}, + FilterGraphOp::SVGFESpecularLightingPoint{..} => {match node.linear {false => 88, true => 89}}, + FilterGraphOp::SVGFESpecularLightingSpot{..} => {match node.linear {false => 90, true => 91}}, + // feTile does not modify color, no linear case + FilterGraphOp::SVGFETile => 92, + FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {match node.linear {false => 94, true => 95}}, + FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {match node.linear {false => 96, true => 97}}, + FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {match node.linear {false => 98, true => 99}}, + FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {match node.linear {false => 100, true => 101}}, + }; + + // This is a bit of an ugly way to do this, but avoids code duplication. + let mut resolve_input = |index: usize, src_task: Option<RenderTaskId>| -> (RenderTaskAddress, [f32; 4]) { + let mut src_task_id = RenderTaskId::INVALID; + let mut resolved_scale_and_offset: [f32; 4] = [0.0; 4]; + if let Some(input) = node.inputs.get(index) { + src_task_id = src_task.unwrap(); + let src_task = &render_tasks[src_task_id]; + + textures.input.colors[index] = src_task.get_texture_source(); + let src_task_size = src_task.location.size(); + let src_scale_x = (src_task_size.width as f32 - input.inflate as f32 * 2.0) / input.subregion.width(); + let src_scale_y = (src_task_size.height as f32 - input.inflate as f32 * 2.0) / input.subregion.height(); + let scale_x = src_scale_x * node.subregion.width(); + let scale_y = src_scale_y * node.subregion.height(); + let offset_x = src_scale_x * (node.subregion.min.x - input.subregion.min.x) + input.inflate as f32; + let offset_y = src_scale_y * (node.subregion.min.y - input.subregion.min.y) + input.inflate as f32; + resolved_scale_and_offset = [ + scale_x, + scale_y, + offset_x, + offset_y]; + } + let address: RenderTaskAddress = src_task_id.into(); + (address, resolved_scale_and_offset) + }; + (instance.input_1_task_address, instance.input_1_content_scale_and_offset) = resolve_input(0, input_1_task); + (instance.input_2_task_address, instance.input_2_content_scale_and_offset) = resolve_input(1, input_2_task); + + // Additional instance modifications for certain filters + match op { + FilterGraphOp::SVGFEOpacity { valuebinding: _, value } => { + // opacity only has one input so we can use the other + // components to store the opacity value + instance.input_2_content_scale_and_offset = [*value, 0.0, 0.0, 0.0]; + }, + FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } | + FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => { + // morphology filters only use one input, so we use the + // second offset coord to store the radius values. + instance.input_2_content_scale_and_offset = [*radius_x, *radius_y, 0.0, 0.0]; + }, + FilterGraphOp::SVGFEFlood { color } => { + // flood filters don't use inputs, so we store color here. + // We can't do the same trick on DropShadow because it does have two + // inputs. + instance.input_2_content_scale_and_offset = [color.r, color.g, color.b, color.a]; + }, + _ => {}, + } + + for (ref mut batch_textures, ref mut batch) in instances.iter_mut() { + if let Some(combined_textures) = batch_textures.combine_textures(textures) { + batch.push(instance); + // Update the batch textures to the newly combined batch textures + *batch_textures = combined_textures; + // is this really the intended behavior? + return; + } + } + + instances.push((textures, vec![instance])); +} + // Information required to do a blit from a source to a target. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] |