diff --git a/webrender/res/brush_image.glsl b/webrender/res/brush_image.glsl index cd03fede96..adca94181b 100644 --- a/webrender/res/brush_image.glsl +++ b/webrender/res/brush_image.glsl @@ -14,7 +14,7 @@ flat varying vec4 vUvBounds; flat varying vec4 vUvBounds_NoClamp; flat varying vec4 vParams; -#if defined WR_FEATURE_ALPHA_TARGET +#if defined WR_FEATURE_ALPHA_TARGET || defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK flat varying vec4 vColor; #endif @@ -41,6 +41,9 @@ void brush_vs( #if defined WR_FEATURE_COLOR_TARGET vec2 texture_size = vec2(textureSize(sColor0, 0).xy); +#elif defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); + vColor = blur_task.color; #else vec2 texture_size = vec2(textureSize(sColor1, 0).xy); vColor = blur_task.color; @@ -120,6 +123,8 @@ vec4 brush_fs() { #if defined WR_FEATURE_COLOR_TARGET vec4 color = texture(sColor0, vec3(uv, vUv.z)); +#elif defined WR_FEATURE_COLOR_TARGET_ALPHA_MASK + vec4 color = vColor * texture(sColor0, vec3(uv, vUv.z)).a; #else vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r; #endif diff --git a/webrender/res/ps_hardware_composite.glsl b/webrender/res/ps_hardware_composite.glsl index 30f1c70121..253b9b29eb 100644 --- a/webrender/res/ps_hardware_composite.glsl +++ b/webrender/res/ps_hardware_composite.glsl @@ -34,6 +34,6 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw); - oFragColor = texture(sCacheRGBA8, vec3(uv, vUv.z)); + oFragColor = texture(sColor0, vec3(uv, vUv.z)); } #endif diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index e795c38e26..80cc7e27db 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -22,7 +22,7 @@ use frame::FrameId; use glyph_rasterizer::FontInstance; use gpu_cache::GpuCache; use gpu_types::ClipScrollNodeData; -use internal_types::{FastHashMap, FastHashSet}; +use internal_types::{FastHashMap, FastHashSet, RenderPassIndex}; use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace}; use prim_store::{BrushAntiAliasMode, BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu}; use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind}; @@ -1761,7 +1761,7 @@ impl FrameBuilder { let mut deferred_resolves = vec![]; - for pass in &mut passes { + for (pass_index, pass) in passes.iter_mut().enumerate() { let ctx = RenderTargetContext { device_pixel_ratio, prim_store: &self.prim_store, @@ -1776,6 +1776,7 @@ impl FrameBuilder { &mut render_tasks, &mut deferred_resolves, &self.clip_store, + RenderPassIndex(pass_index), ); profile_counters.passes.inc(); diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index f80bde7765..1e04fb8da5 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -32,6 +32,9 @@ pub type FastHashSet = HashSet>; #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct CacheTextureId(pub usize); +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct RenderPassIndex(pub usize); + // Represents the source for a texture. // These are passed from throughout the // pipeline until they reach the rendering @@ -45,6 +48,10 @@ pub enum SourceTexture { External(ExternalImageData), CacheA8, CacheRGBA8, + // XXX Remove this once RenderTaskCacheA8 is used. + #[allow(dead_code)] + RenderTaskCacheA8(RenderPassIndex), + RenderTaskCacheRGBA8(RenderPassIndex), } pub const ORTHO_NEAR_PLANE: f32 = -1000000.0; diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index b707f377a8..b12d902353 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -67,7 +67,10 @@ pub enum PictureKind { // If a mix-blend-mode, contains the render task for // the readback of the framebuffer that we use to sample // from in the mix-blend-mode shader. - readback_render_task_id: Option, + // For drop-shadow filter, this will store the original + // picture task which would be rendered on screen after + // blur pass. + secondary_render_task_id: Option, /// How this picture should be composited. /// If None, don't composite - just draw directly on parent surface. composite_mode: Option, @@ -186,7 +189,7 @@ impl PicturePrimitive { runs: Vec::new(), render_task_id: None, kind: PictureKind::Image { - readback_render_task_id: None, + secondary_render_task_id: None, composite_mode, is_in_3d_context, frame_output_pipeline_id, @@ -236,6 +239,11 @@ impl PicturePrimitive { let inflate_size = blur_radius * BLUR_SAMPLE_SCALE; local_content_rect.inflate(inflate_size, inflate_size) } + Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, _))) => { + let inflate_size = blur_radius * BLUR_SAMPLE_SCALE; + local_content_rect.inflate(inflate_size, inflate_size) + .translate(&offset) + } _ => { local_content_rect } @@ -304,7 +312,7 @@ impl PicturePrimitive { ) { match self.kind { PictureKind::Image { - ref mut readback_render_task_id, + ref mut secondary_render_task_id, composite_mode, .. } => { @@ -341,6 +349,37 @@ impl PicturePrimitive { let blur_render_task_id = render_tasks.add(blur_render_task); self.render_task_id = Some(blur_render_task_id); } + Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => { + let picture_task = RenderTask::new_picture( + Some(prim_screen_rect.size), + prim_index, + RenderTargetKind::Color, + prim_screen_rect.origin.x as f32 - offset.x, + prim_screen_rect.origin.y as f32 - offset.y, + PremultipliedColorF::TRANSPARENT, + ClearMode::Transparent, + self.rasterization_kind, + child_tasks, + None, + ); + + let blur_std_deviation = blur_radius * prim_context.device_pixel_ratio; + let picture_task_id = render_tasks.add(picture_task); + + let blur_render_task = RenderTask::new_blur( + blur_std_deviation, + picture_task_id, + render_tasks, + RenderTargetKind::Color, + &[], + ClearMode::Transparent, + color.premultiplied(), + None, + ); + + *secondary_render_task_id = Some(picture_task_id); + self.render_task_id = Some(render_tasks.add(blur_render_task)); + } Some(PictureCompositeMode::MixBlend(..)) => { let picture_task = RenderTask::new_picture( Some(prim_screen_rect.size), @@ -357,7 +396,7 @@ impl PicturePrimitive { let readback_task_id = render_tasks.add(RenderTask::new_readback(*prim_screen_rect)); - *readback_render_task_id = Some(readback_task_id); + *secondary_render_task_id = Some(readback_task_id); parent_tasks.push(readback_task_id); self.render_task_id = Some(render_tasks.add(picture_task)); diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index df3d624373..e2a743bc93 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -9,6 +9,7 @@ use clip::{ClipSourcesWeakHandle}; use clip_scroll_tree::CoordinateSystemId; use euclid::TypedSize2D; use gpu_types::{ClipScrollNodeIndex}; +use internal_types::RenderPassIndex; use picture::RasterizationSpace; use prim_store::{PrimitiveIndex}; #[cfg(feature = "debugger")] @@ -241,6 +242,7 @@ pub struct RenderTask { pub children: Vec, pub kind: RenderTaskKind, pub clear_mode: ClearMode, + pub pass_index: Option, } impl RenderTask { @@ -276,6 +278,7 @@ impl RenderTask { rasterization_kind, }), clear_mode, + pass_index: None, } } @@ -286,6 +289,7 @@ impl RenderTask { location: RenderTaskLocation::Dynamic(None, screen_rect.size), kind: RenderTaskKind::Readback(screen_rect), clear_mode: ClearMode::Transparent, + pass_index: None, } } @@ -305,6 +309,7 @@ impl RenderTask { coordinate_system_id: prim_coordinate_system_id, }), clear_mode: ClearMode::One, + pass_index: None, } } @@ -375,6 +380,7 @@ impl RenderTask { scale_factor, }), clear_mode, + pass_index: None, }; let blur_task_v_id = render_tasks.add(blur_task_v); @@ -394,6 +400,7 @@ impl RenderTask { scale_factor, }), clear_mode, + pass_index: None, }; blur_task_h @@ -417,6 +424,7 @@ impl RenderTask { RenderTargetKind::Color => ClearMode::Transparent, RenderTargetKind::Alpha => ClearMode::One, }, + pass_index: None, } } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index f438d7538c..7f292d1355 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -40,7 +40,7 @@ use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use gpu_types::PrimitiveInstance; use internal_types::{BatchTextures, SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE}; use internal_types::{CacheTextureId, FastHashMap, RenderedDocument, ResultMsg, TextureUpdateOp}; -use internal_types::{DebugOutput, RenderTargetInfo, TextureUpdateList, TextureUpdateSource}; +use internal_types::{DebugOutput, RenderPassIndex, RenderTargetInfo, TextureUpdateList, TextureUpdateSource}; use profiler::{BackendProfileCounters, Profiler}; use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers}; use query::{GpuProfiler, GpuTimer}; @@ -65,8 +65,8 @@ use std::thread; use texture_cache::TextureCache; use thread_profiler::{register_thread_with_profiler, write_profile}; use tiling::{AlphaRenderTarget, ColorRenderTarget}; -use tiling::{RenderPass, RenderPassKind, RenderTargetKind, RenderTargetList}; -use tiling::{BatchKey, BatchKind, BrushBatchKind, Frame, RenderTarget, ScalingInfo, TransformBatchKind}; +use tiling::{RenderPass, RenderPassKind, RenderTargetList}; +use tiling::{BatchKey, BatchKind, BrushBatchKind, BrushImageSourceKind, Frame, RenderTarget, ScalingInfo, TransformBatchKind}; use time::precise_time_ns; use util::TransformedRectKind; @@ -551,6 +551,8 @@ impl CpuProfile { } } +struct RenderTargetPoolId(usize); + struct SourceTextureResolver { /// A vector for fast resolves of texture cache IDs to /// native texture IDs. This maps to a free-list managed @@ -573,6 +575,11 @@ struct SourceTextureResolver { /// The current cache textures. cache_rgba8_texture: Option, cache_a8_texture: Option, + + pass_rgba8_textures: FastHashMap, + pass_a8_textures: FastHashMap, + + render_target_pool: Vec, } impl SourceTextureResolver { @@ -595,6 +602,9 @@ impl SourceTextureResolver { dummy_cache_texture, cache_a8_texture: None, cache_rgba8_texture: None, + pass_rgba8_textures: FastHashMap::default(), + pass_a8_textures: FastHashMap::default(), + render_target_pool: Vec::new(), } } @@ -604,27 +614,44 @@ impl SourceTextureResolver { for texture in self.cache_texture_map { device.delete_texture(texture); } + + for texture in self.render_target_pool { + device.delete_texture(texture); + } } - fn begin_frame(&self) { + fn begin_frame(&mut self) { assert!(self.cache_rgba8_texture.is_none()); assert!(self.cache_a8_texture.is_none()); + + self.pass_rgba8_textures.clear(); + self.pass_a8_textures.clear(); } - fn end_frame(&mut self, pool: &mut Vec) { + fn end_frame(&mut self, pass_index: RenderPassIndex) { // return the cached targets to the pool - self.end_pass(None, None, pool) + self.end_pass(None, None, pass_index) } fn end_pass( &mut self, a8_texture: Option, rgba8_texture: Option, - pool: &mut Vec, + pass_index: RenderPassIndex, ) { // If we have cache textures from previous pass, return them to the pool. - pool.extend(self.cache_rgba8_texture.take()); - pool.extend(self.cache_a8_texture.take()); + // Also assign the pool index of those cache textures to last pass's index because this is + // the result of last pass. + if let Some(texture) = self.cache_rgba8_texture.take() { + self.pass_rgba8_textures.insert( + RenderPassIndex(pass_index.0 - 1), RenderTargetPoolId(self.render_target_pool.len())); + self.render_target_pool.push(texture); + } + if let Some(texture) = self.cache_a8_texture.take() { + self.pass_a8_textures.insert( + RenderPassIndex(pass_index.0 - 1), RenderTargetPoolId(self.render_target_pool.len())); + self.render_target_pool.push(texture); + } // We have another pass to process, make these textures available // as inputs to the next pass. @@ -658,6 +685,18 @@ impl SourceTextureResolver { let texture = &self.cache_texture_map[index.0]; device.bind_texture(sampler, texture); } + SourceTexture::RenderTaskCacheRGBA8(pass_index) => { + let pool_index = self.pass_rgba8_textures + .get(&pass_index) + .expect("BUG: pass_index doesn't map to pool_index"); + device.bind_texture(sampler, &self.render_target_pool[pool_index.0]) + } + SourceTexture::RenderTaskCacheA8(pass_index) => { + let pool_index = self.pass_a8_textures + .get(&pass_index) + .expect("BUG: pass_index doesn't map to pool_index"); + device.bind_texture(sampler, &self.render_target_pool[pool_index.0]) + } } } @@ -681,6 +720,18 @@ impl SourceTextureResolver { panic!("BUG: External textures cannot be resolved, they can only be bound."); } SourceTexture::TextureCache(index) => Some(&self.cache_texture_map[index.0]), + SourceTexture::RenderTaskCacheRGBA8(pass_index) => { + let pool_index = self.pass_rgba8_textures + .get(&pass_index) + .expect("BUG: pass_index doesn't map to pool_index"); + Some(&self.render_target_pool[pool_index.0]) + }, + SourceTexture::RenderTaskCacheA8(pass_index) => { + let pool_index = self.pass_a8_textures + .get(&pass_index) + .expect("BUG: pass_index doesn't map to pool_index"); + Some(&self.render_target_pool[pool_index.0]) + }, } } } @@ -1349,6 +1400,7 @@ pub struct Renderer { brush_mask_corner: LazilyCompiledShader, brush_mask_rounded_rect: LazilyCompiledShader, brush_image_rgba8: BrushShader, + brush_image_rgba8_alpha_mask: BrushShader, brush_image_a8: BrushShader, brush_solid: BrushShader, @@ -1394,8 +1446,6 @@ pub struct Renderer { profiler: Profiler, last_time: u64, - render_target_pool: Vec, - gpu_profile: GpuProfiler, prim_vao: VAO, blur_vao: VAO, @@ -1574,6 +1624,13 @@ impl Renderer { options.precache_shaders) }; + let brush_image_rgba8_alpha_mask = try!{ + BrushShader::new("brush_image", + &mut device, + &["COLOR_TARGET_ALPHA_MASK"], + options.precache_shaders) + }; + let cs_blur_a8 = try!{ LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur), "cs_blur", @@ -1998,6 +2055,7 @@ impl Renderer { brush_mask_corner, brush_mask_rounded_rect, brush_image_rgba8, + brush_image_rgba8_alpha_mask, brush_image_a8, brush_solid, cs_clip_rectangle, @@ -2027,7 +2085,6 @@ impl Renderer { clear_color: options.clear_color, enable_clear_scissor: options.enable_clear_scissor, last_time: 0, - render_target_pool: Vec::new(), gpu_profile, prim_vao, blur_vao, @@ -2778,8 +2835,9 @@ impl Renderer { } BrushBatchKind::Image(target_kind) => { let shader = match target_kind { - RenderTargetKind::Alpha => &mut self.brush_image_a8, - RenderTargetKind::Color => &mut self.brush_image_rgba8, + BrushImageSourceKind::Alpha => &mut self.brush_image_a8, + BrushImageSourceKind::Color => &mut self.brush_image_rgba8, + BrushImageSourceKind::ColorAlphaMask => &mut self.brush_image_rgba8_alpha_mask, }; shader.bind( &mut self.device, @@ -3684,7 +3742,7 @@ impl Renderer { num_layers: list.targets.len() as _, format: list.format, }; - let index = self.render_target_pool + let index = self.texture_resolver.render_target_pool .iter() .position(|texture| { selector == TargetSelector { @@ -3694,14 +3752,14 @@ impl Renderer { } }); match index { - Some(pos) => self.render_target_pool.swap_remove(pos), + Some(pos) => self.texture_resolver.render_target_pool.swap_remove(pos), None => return, } } else { if list.texture.is_some() { return } - match self.render_target_pool.pop() { + match self.texture_resolver.render_target_pool.pop() { Some(texture) => texture, None => self.device.create_texture(TextureTarget::Array), } @@ -3885,7 +3943,7 @@ impl Renderer { self.texture_resolver.end_pass( cur_alpha, cur_color, - &mut self.render_target_pool, + RenderPassIndex(pass_index), ); // After completing the first pass, make the A8 target available as an @@ -3900,7 +3958,7 @@ impl Renderer { } } - self.texture_resolver.end_frame(&mut self.render_target_pool); + self.texture_resolver.end_frame(RenderPassIndex(frame.passes.len())); self.draw_render_target_debug(framebuffer_size); self.draw_texture_cache_debug(framebuffer_size); self.draw_epoch_debug(); @@ -3967,7 +4025,7 @@ impl Renderer { let mut spacing = 16; let mut size = 512; let fb_width = framebuffer_size.width as i32; - let num_layers: i32 = self.render_target_pool + let num_layers: i32 = self.texture_resolver.render_target_pool .iter() .map(|texture| texture.get_render_target_layer_count() as i32) .sum(); @@ -3979,7 +4037,7 @@ impl Renderer { } let mut target_index = 0; - for texture in &self.render_target_pool { + for texture in &self.texture_resolver.render_target_pool { let dimensions = texture.get_dimensions(); let src_rect = DeviceIntRect::new(DeviceIntPoint::zero(), dimensions.to_i32()); @@ -4121,9 +4179,6 @@ impl Renderer { } self.node_data_texture.deinit(&mut self.device); self.render_task_texture.deinit(&mut self.device); - for texture in self.render_target_pool { - self.device.delete_texture(texture); - } self.device.delete_pbo(self.texture_cache_upload_pbo); self.texture_resolver.deinit(&mut self.device); self.device.delete_vao(self.prim_vao); @@ -4137,6 +4192,7 @@ impl Renderer { self.brush_mask_rounded_rect.deinit(&mut self.device); self.brush_mask_corner.deinit(&mut self.device); self.brush_image_rgba8.deinit(&mut self.device); + self.brush_image_rgba8_alpha_mask.deinit(&mut self.device); self.brush_image_a8.deinit(&mut self.device); self.brush_solid.deinit(&mut self.device); self.cs_clip_rectangle.deinit(&mut self.device); diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index 99bf9bb89e..fc5f665ccb 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -162,7 +162,8 @@ impl FilterOpHelpers for FilterOp { FilterOp::HueRotate(..) | FilterOp::Invert(..) | FilterOp::Saturate(..) | - FilterOp::Sepia(..) => true, + FilterOp::Sepia(..) | + FilterOp::DropShadow(..) => true, FilterOp::Opacity(_, amount) => { amount > OPACITY_EPSILON } @@ -180,6 +181,9 @@ impl FilterOpHelpers for FilterOp { FilterOp::Opacity(_, amount) => amount >= 1.0, FilterOp::Saturate(amount) => amount == 1.0, FilterOp::Sepia(amount) => amount == 0.0, + FilterOp::DropShadow(offset, blur, _) => { + offset.x == 0.0 && offset.y == 0.0 && blur == 0.0 + } } } } diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 9120164dd3..b780d5e7aa 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadiusKind, ClipId, ColorF, DeviceIntPoint, ImageKey}; -use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; +use api::{DeviceIntRect, DeviceIntSize, device_length, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; use api::{DocumentLayer, ExternalImageType, FilterOp, FontRenderMode}; use api::{ImageFormat, ImageRendering}; use api::{LayerRect, MixBlendMode, PipelineId}; @@ -20,7 +20,7 @@ use gpu_types::{BlurDirection, BlurInstance, BrushInstance, BrushImageKind, Clip use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData}; use internal_types::{FastHashMap, SourceTexture}; -use internal_types::{BatchTextures}; +use internal_types::{BatchTextures, RenderPassIndex}; use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace}; use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; @@ -148,9 +148,6 @@ pub struct ScrollbarPrimitive { #[derive(Debug, Copy, Clone)] pub struct RenderTargetIndex(pub usize); -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct RenderPassIndex(isize); - #[derive(Debug)] struct DynamicTaskInfo { task_id: RenderTaskId, @@ -627,7 +624,8 @@ fn add_to_batch( match picture.kind { PictureKind::TextShadow { .. } => { let kind = BatchKind::Brush( - BrushBatchKind::Image(picture.target_kind()), + BrushBatchKind::Image( + BrushImageSourceKind::from_render_target_kind(picture.target_kind())), ); let key = BatchKey::new(kind, blend_mode, textures); let batch = batch_list.get_suitable_batch(key, item_bounding_rect); @@ -647,7 +645,8 @@ fn add_to_batch( } PictureKind::BoxShadow { radii_kind, .. } => { let kind = BatchKind::Brush( - BrushBatchKind::Image(picture.target_kind()), + BrushBatchKind::Image( + BrushImageSourceKind::from_render_target_kind(picture.target_kind())), ); let key = BatchKey::new(kind, blend_mode, textures); let batch = batch_list.get_suitable_batch(key, item_bounding_rect); @@ -676,7 +675,7 @@ fn add_to_batch( } PictureKind::Image { composite_mode, - readback_render_task_id, + secondary_render_task_id, is_in_3d_context, reference_frame_id, real_local_rect, @@ -717,7 +716,7 @@ fn add_to_batch( let key = BatchKey::new( BatchKind::HardwareComposite, BlendMode::PremultipliedAlpha, - BatchTextures::no_texture(), + BatchTextures::render_target_cache(), ); let batch = batch_list.get_suitable_batch(key, &item_bounding_rect); let instance = CompositePrimitiveInstance::new( @@ -733,6 +732,61 @@ fn add_to_batch( batch.push(PrimitiveInstance::from(instance)); } + FilterOp::DropShadow(offset, _, _) => { + let kind = BatchKind::Brush( + BrushBatchKind::Image(BrushImageSourceKind::ColorAlphaMask), + ); + let key = BatchKey::new(kind, blend_mode, textures); + + let instance = BrushInstance { + picture_address: task_address, + prim_address: prim_cache_address, + clip_id, + scroll_id, + clip_task_address, + z, + segment_kind: 0, + user_data0: cache_task_address.0 as i32, + user_data1: BrushImageKind::Simple as i32, + }; + + { + let batch = batch_list.get_suitable_batch(key, item_bounding_rect); + batch.push(PrimitiveInstance::from(instance)); + } + + let secondary_id = secondary_render_task_id.expect("no secondary!?"); + let render_task = &render_tasks[secondary_id]; + let secondary_task_address = render_tasks.get_task_address(secondary_id); + let render_pass_index = render_task.pass_index.expect("no render_pass_index!?"); + let secondary_textures = BatchTextures { + colors: [ + SourceTexture::RenderTaskCacheRGBA8(render_pass_index), + SourceTexture::Invalid, + SourceTexture::Invalid, + ], + }; + let key = BatchKey::new( + BatchKind::HardwareComposite, + BlendMode::PremultipliedAlpha, + secondary_textures, + ); + let batch = batch_list.get_suitable_batch(key, &item_bounding_rect); + let device_offset_x = device_length(offset.x, ctx.device_pixel_ratio); + let device_offset_y = device_length(offset.y, ctx.device_pixel_ratio); + let instance = CompositePrimitiveInstance::new( + task_address, + secondary_task_address, + RenderTaskAddress(0), + item_bounding_rect.origin.x - device_offset_x.0, + item_bounding_rect.origin.y - device_offset_y.0, + z, + item_bounding_rect.size.width, + item_bounding_rect.size.height, + ); + + batch.push(PrimitiveInstance::from(instance)); + } _ => { let key = BatchKey::new( BatchKind::Blend, @@ -751,6 +805,7 @@ fn add_to_batch( FilterOp::Sepia(amount) => (6, amount), FilterOp::Brightness(amount) => (7, amount), FilterOp::Opacity(_, amount) => (8, amount), + FilterOp::DropShadow(..) => unreachable!(), }; let amount = (amount * 65535.0).round() as i32; @@ -772,7 +827,7 @@ fn add_to_batch( } } PictureCompositeMode::MixBlend(mode) => { - let backdrop_id = readback_render_task_id.expect("no backdrop!?"); + let backdrop_id = secondary_render_task_id.expect("no backdrop!?"); let key = BatchKey::new( BatchKind::Composite { @@ -805,7 +860,7 @@ fn add_to_batch( let key = BatchKey::new( BatchKind::HardwareComposite, BlendMode::PremultipliedAlpha, - BatchTextures::no_texture(), + BatchTextures::render_target_cache(), ); let batch = batch_list.get_suitable_batch(key, &item_bounding_rect); let instance = CompositePrimitiveInstance::new( @@ -1796,6 +1851,7 @@ impl RenderPass { render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, clip_store: &ClipStore, + pass_index: RenderPassIndex, ) { profile_scope!("RenderPass::build"); @@ -1803,6 +1859,7 @@ impl RenderPass { RenderPassKind::MainFramebuffer(ref mut target) => { for &task_id in &self.tasks { assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color); + render_tasks[task_id].pass_index = Some(pass_index); target.add_task(task_id, ctx, gpu_cache, render_tasks, clip_store); } target.build(ctx, gpu_cache, render_tasks, deferred_resolves); @@ -1812,6 +1869,7 @@ impl RenderPass { for &task_id in &self.tasks { let target_kind = { let task = &mut render_tasks[task_id]; + task.pass_index = Some(pass_index); let target_kind = task.target_kind(); // Find a target to assign this task to, or create a new @@ -1880,9 +1938,25 @@ pub enum TransformBatchKind { Line, } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum BrushImageSourceKind { + Alpha, + Color, + ColorAlphaMask, +} + +impl BrushImageSourceKind { + pub fn from_render_target_kind(render_target_kind: RenderTargetKind) -> BrushImageSourceKind { + match render_target_kind { + RenderTargetKind::Color => BrushImageSourceKind::Color, + RenderTargetKind::Alpha => BrushImageSourceKind::Alpha, + } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum BrushBatchKind { - Image(RenderTargetKind), + Image(BrushImageSourceKind), Solid, } diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index cb94876a81..bd2e449490 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -458,6 +458,7 @@ pub enum FilterOp { Opacity(PropertyBinding, f32), Saturate(f32), Sepia(f32), + DropShadow(LayoutVector2D, f32, ColorF), } #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] diff --git a/wrench/reftests/filters/filter-drop-shadow.png b/wrench/reftests/filters/filter-drop-shadow.png new file mode 100644 index 0000000000..95c910f124 Binary files /dev/null and b/wrench/reftests/filters/filter-drop-shadow.png differ diff --git a/wrench/reftests/filters/filter-drop-shadow.yaml b/wrench/reftests/filters/filter-drop-shadow.yaml new file mode 100644 index 0000000000..59f90b42da --- /dev/null +++ b/wrench/reftests/filters/filter-drop-shadow.yaml @@ -0,0 +1,9 @@ +--- +root: + items: + - type: stacking-context + bounds: [100, 100, 400, 400] + filters: drop-shadow([73, 73], 20, [255, 0, 0, 1]) + items: + - image: "firefox.png" + bounds: 0 0 256 256 diff --git a/wrench/reftests/filters/reftest.list b/wrench/reftests/filters/reftest.list index 48e3a3fa3d..e32cf622e6 100644 --- a/wrench/reftests/filters/reftest.list +++ b/wrench/reftests/filters/reftest.list @@ -29,3 +29,4 @@ == filter-hue-rotate-1.yaml filter-hue-rotate-1-ref.yaml == filter-hue-rotate-alpha-1.yaml filter-hue-rotate-alpha-1-ref.yaml == filter-long-chain.yaml filter-long-chain.png +== filter-drop-shadow.yaml filter-drop-shadow.png diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index a986305e49..91bfce9fa4 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -5,6 +5,6 @@ # but fails when all the tests are run fuzzy(1,2) == rotated-image.yaml rotated-image.png == singular.yaml singular-ref.yaml -fuzzy(1,41) == perspective.yaml perspective.png +fuzzy(1,82) == perspective.yaml perspective.png == prim-suite.yaml prim-suite.png == segments-bug.yaml segments-bug-ref.yaml diff --git a/wrench/src/parse_function.rs b/wrench/src/parse_function.rs index 82267064ce..2b2328b855 100644 --- a/wrench/src/parse_function.rs +++ b/wrench/src/parse_function.rs @@ -64,8 +64,22 @@ pub fn parse_function(s: &str) -> (&str, Vec<&str>, &str) { p.skip_whitespace(); let mut end = p.start; + let mut bracket_count: i32 = 0; while let Some(k) = p.o { - if !acceptable_arg_character(k.1) { + let prev_bracket_count = bracket_count; + if k.1 == '[' { + bracket_count = bracket_count + 1; + } else if k.1 == ']' { + bracket_count = bracket_count - 1; + } + + if bracket_count < 0 { + println!("Unexpected closing bracket"); + break; + } + + let not_in_bracket = bracket_count == 0 && prev_bracket_count == 0; + if !acceptable_arg_character(k.1) && not_in_bracket { break; } end = k.0 + k.1.len_utf8(); @@ -100,4 +114,8 @@ fn test() { assert_eq!(parse_function(" rotate (40)").0, "rotate"); assert_eq!(parse_function(" rotate ( 40 )").1[0], "40"); assert_eq!(parse_function("rotate(-40.0)").1[0], "-40.0"); + assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[0], "0"); + assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[1], "[1, 2, 3, 4]"); + assert_eq!(parse_function("drop-shadow(0, [1, 2, 3, 4], 5)").1[2], "5"); + assert_eq!(parse_function("drop-shadow(0, [1, 2, [3, 4]], 5)").1[1], "[1, 2, [3, 4]]"); } diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index 3a85b90989..95ae535eeb 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -210,6 +210,12 @@ fn write_sc(parent: &mut Table, sc: &StackingContext, properties: &SceneProperti } 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)))) + } } } diff --git a/wrench/src/yaml_helper.rs b/wrench/src/yaml_helper.rs index de9e8b16fa..a5841f665e 100644 --- a/wrench/src/yaml_helper.rs +++ b/wrench/src/yaml_helper.rs @@ -8,7 +8,7 @@ use parse_function::parse_function; use std::f32; use std::str::FromStr; use webrender::api::*; -use yaml_rust::Yaml; +use yaml_rust::{Yaml, YamlLoader}; pub trait YamlHelper { fn as_f32(&self) -> Option; @@ -564,6 +564,14 @@ impl YamlHelper for Yaml { ("sepia", ref args, _) if args.len() == 1 => { Some(FilterOp::Sepia(args[0].parse().unwrap())) } + ("drop-shadow", ref args, _) if args.len() == 3 => { + let str = format!("---\noffset: {}\nblur-radius: {}\ncolor: {}\n", args[0], args[1], args[2]); + let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse drop-shadow"); + let yaml = yaml_doc.pop().unwrap(); + Some(FilterOp::DropShadow(yaml["offset"].as_vector().unwrap(), + yaml["blur-radius"].as_f32().unwrap(), + yaml["color"].as_colorf().unwrap())) + } (_, _, _) => None, } } else {