| author | Tim Nguyen <ntim.bugs@gmail.com> |
| Wed, 12 Feb 2020 18:08:32 +0000 | |
| changeset 513590 | 62a1a471e01c5e9607b485cce93337cb1c237e38 |
| parent 513589 | a8cac77208156c4a7ce0a43b2685bfd975404fd5 |
| child 513591 | 5e2bfa7af7e62db4587f9b0ca58a3084e1f3a9ed |
| push id | 37118 |
| push user | rmaries@mozilla.com |
| push date | Thu, 13 Feb 2020 03:57:45 +0000 |
| treeherder | mozilla-central@2f6870dd1b99 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | gw, nical, emilio |
| bugs | 1614890 |
| milestone | 75.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -1190,16 +1190,27 @@ void DisplayListBuilder::PushRadialGradi wr::ExtendMode aExtendMode, const wr::LayoutSize aTileSize, const wr::LayoutSize aTileSpacing) { wr_dp_push_radial_gradient( mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible, &mCurrentSpaceAndClipChain, aCenter, aRadius, aStops.Elements(), aStops.Length(), aExtendMode, aTileSize, aTileSpacing); } +void DisplayListBuilder::PushConicGradient( + const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, + bool aIsBackfaceVisible, const wr::LayoutPoint& aCenter, const float aAngle, + const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode, + const wr::LayoutSize aTileSize, const wr::LayoutSize aTileSpacing) { + wr_dp_push_conic_gradient(mWrState, aBounds, MergeClipLeaf(aClip), + aIsBackfaceVisible, &mCurrentSpaceAndClipChain, + aCenter, aAngle, aStops.Elements(), aStops.Length(), + aExtendMode, aTileSize, aTileSpacing); +} + void DisplayListBuilder::PushImage( const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, wr::ImageRendering aFilter, wr::ImageKey aImage, bool aPremultipliedAlpha, const wr::ColorF& aColor) { wr::LayoutRect clip = MergeClipLeaf(aClip); WRDL_LOG("PushImage b=%s cl=%s\n", mWrState, Stringify(aBounds).c_str(), Stringify(clip).c_str()); wr_dp_push_image(mWrState, aBounds, clip, aIsBackfaceVisible, @@ -1312,16 +1323,28 @@ void DisplayListBuilder::PushBorderRadia const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode, const wr::LayoutSideOffsets& aOutset) { wr_dp_push_border_radial_gradient( mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible, &mCurrentSpaceAndClipChain, aWidths, aFill, aCenter, aRadius, aStops.Elements(), aStops.Length(), aExtendMode, aOutset); } +void DisplayListBuilder::PushBorderConicGradient( + const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, + bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill, + const wr::LayoutPoint& aCenter, const float aAngle, + const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode, + const wr::LayoutSideOffsets& aOutset) { + wr_dp_push_border_conic_gradient( + mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible, + &mCurrentSpaceAndClipChain, aWidths, aFill, aCenter, aAngle, + aStops.Elements(), aStops.Length(), aExtendMode, aOutset); +} + void DisplayListBuilder::PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::ColorF& aColor, wr::FontInstanceKey aFontKey, Range<const wr::GlyphInstance> aGlyphBuffer, const wr::GlyphOptions* aGlyphOptions) { wr_dp_push_text(mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
--- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -488,16 +488,24 @@ class DisplayListBuilder final { const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::LayoutPoint& aCenter, const wr::LayoutSize& aRadius, const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode, const wr::LayoutSize aTileSize, const wr::LayoutSize aTileSpacing); + void PushConicGradient(const wr::LayoutRect& aBounds, + const wr::LayoutRect& aClip, bool aIsBackfaceVisible, + const wr::LayoutPoint& aCenter, const float aAngle, + const nsTArray<wr::GradientStop>& aStops, + wr::ExtendMode aExtendMode, + const wr::LayoutSize aTileSize, + const wr::LayoutSize aTileSpacing); + void PushImage(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, wr::ImageRendering aFilter, wr::ImageKey aImage, bool aPremultipliedAlpha = true, const wr::ColorF& aColor = wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f}); void PushRepeatingImage( const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::LayoutSize& aStretchSize, @@ -552,16 +560,23 @@ class DisplayListBuilder final { void PushBorderRadialGradient( const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill, const wr::LayoutPoint& aCenter, const wr::LayoutSize& aRadius, const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode, const wr::LayoutSideOffsets& aOutset); + void PushBorderConicGradient( + const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, + bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill, + const wr::LayoutPoint& aCenter, const float aAngle, + const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode, + const wr::LayoutSideOffsets& aOutset); + void PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::ColorF& aColor, wr::FontInstanceKey aFontKey, Range<const wr::GlyphInstance> aGlyphBuffer, const wr::GlyphOptions* aGlyphOptions = nullptr); void PushLine(const wr::LayoutRect& aClip, bool aIsBackfaceVisible, const wr::Line& aLine);
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -3360,16 +3360,79 @@ pub extern "C" fn wr_dp_push_border_radi &prim_info, rect, widths.into(), border_details, ); } #[no_mangle] +pub extern "C" fn wr_dp_push_border_conic_gradient(state: &mut WrState, + rect: LayoutRect, + clip: LayoutRect, + is_backface_visible: bool, + parent: &WrSpaceAndClipChain, + widths: LayoutSideOffsets, + fill: bool, + center: LayoutPoint, + angle: f32, + stops: *const GradientStop, + stops_count: usize, + extend_mode: ExtendMode, + outset: LayoutSideOffsets) { + debug_assert!(unsafe { is_in_main_thread() }); + + let stops_slice = unsafe { make_slice(stops, stops_count) }; + let stops_vector = stops_slice.to_owned(); + + let slice = SideOffsets2D::new( + widths.top as i32, + widths.right as i32, + widths.bottom as i32, + widths.left as i32, + ); + + let gradient = state.frame_builder.dl_builder.create_conic_gradient( + center.into(), + angle, + stops_vector, + extend_mode.into() + ); + + let border_details = BorderDetails::NinePatch(NinePatchBorder { + source: NinePatchBorderSource::ConicGradient(gradient), + width: rect.size.width as i32, + height: rect.size.height as i32, + slice, + fill, + outset: outset.into(), + repeat_horizontal: RepeatMode::Stretch, + repeat_vertical: RepeatMode::Stretch, + }); + + let space_and_clip = parent.to_webrender(state.pipeline_id); + + let prim_info = CommonItemProperties { + clip_rect: clip, + clip_id: space_and_clip.clip_id, + spatial_id: space_and_clip.spatial_id, + flags: prim_flags(is_backface_visible), + hit_info: state.current_tag, + item_key: state.current_item_key, + }; + + state.frame_builder.dl_builder.push_border( + &prim_info, + rect, + widths.into(), + border_details, + ); +} + +#[no_mangle] pub extern "C" fn wr_dp_push_linear_gradient(state: &mut WrState, rect: LayoutRect, clip: LayoutRect, is_backface_visible: bool, parent: &WrSpaceAndClipChain, start_point: LayoutPoint, end_point: LayoutPoint, stops: *const GradientStop, @@ -3449,16 +3512,60 @@ pub extern "C" fn wr_dp_push_radial_grad &prim_info, rect, gradient, tile_size, tile_spacing); } #[no_mangle] +pub extern "C" fn wr_dp_push_conic_gradient(state: &mut WrState, + rect: LayoutRect, + clip: LayoutRect, + is_backface_visible: bool, + parent: &WrSpaceAndClipChain, + center: LayoutPoint, + angle: f32, + stops: *const GradientStop, + stops_count: usize, + extend_mode: ExtendMode, + tile_size: LayoutSize, + tile_spacing: LayoutSize) { + debug_assert!(unsafe { is_in_main_thread() }); + + let stops_slice = unsafe { make_slice(stops, stops_count) }; + let stops_vector = stops_slice.to_owned(); + + let gradient = state.frame_builder + .dl_builder + .create_conic_gradient(center.into(), + angle, + stops_vector, + extend_mode.into()); + + let space_and_clip = parent.to_webrender(state.pipeline_id); + + let prim_info = CommonItemProperties { + clip_rect: clip, + clip_id: space_and_clip.clip_id, + spatial_id: space_and_clip.spatial_id, + flags: prim_flags(is_backface_visible), + hit_info: state.current_tag, + item_key: state.current_item_key, + }; + + state.frame_builder.dl_builder.push_conic_gradient( + &prim_info, + rect, + gradient, + tile_size, + tile_spacing); +} + +#[no_mangle] pub extern "C" fn wr_dp_push_box_shadow(state: &mut WrState, _rect: LayoutRect, clip: LayoutRect, is_backface_visible: bool, parent: &WrSpaceAndClipChain, box_bounds: LayoutRect, offset: LayoutVector2D, color: ColorF,
--- a/gfx/webrender_bindings/webrender_ffi.h +++ b/gfx/webrender_bindings/webrender_ffi.h @@ -44,16 +44,17 @@ bool gecko_profiler_thread_is_being_prof macro(prim); \ macro(normal_border); \ macro(image_border); \ macro(image); \ macro(yuv_image); \ macro(line_decoration); \ macro(linear_grad); \ macro(radial_grad); \ + macro(conic_grad); \ macro(picture); \ macro(text_run); \ macro(filterdata); \ macro(backdrop); // Prelude of types necessary before including webrender_ffi_generated.h namespace mozilla { namespace wr {
--- a/gfx/wr/webrender/res/brush.glsl +++ b/gfx/wr/webrender/res/brush.glsl @@ -84,16 +84,17 @@ void name( // Forward-declare all brush vertex entry points. FWD_DECLARE_VS_FUNCTION(image_brush_vs) FWD_DECLARE_VS_FUNCTION(solid_brush_vs) FWD_DECLARE_VS_FUNCTION(blend_brush_vs) FWD_DECLARE_VS_FUNCTION(mix_blend_brush_vs) FWD_DECLARE_VS_FUNCTION(linear_gradient_brush_vs) FWD_DECLARE_VS_FUNCTION(radial_gradient_brush_vs) +FWD_DECLARE_VS_FUNCTION(conic_gradient_brush_vs) FWD_DECLARE_VS_FUNCTION(yuv_brush_vs) FWD_DECLARE_VS_FUNCTION(opacity_brush_vs) void multi_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, RectWithSize segment_rect, @@ -253,16 +254,17 @@ struct Fragment { // Foward-declare all brush entry-points. Fragment image_brush_fs(); Fragment solid_brush_fs(); Fragment blend_brush_fs(); Fragment mix_blend_brush_fs(); Fragment linear_gradient_brush_fs(); Fragment radial_gradient_brush_fs(); +Fragment conic_gradient_brush_fs(); Fragment yuv_brush_fs(); Fragment opacity_brush_fs(); Fragment multi_brush_fs(int brush_kind); void main(void) { #ifdef WR_FEATURE_DEBUG_OVERDRAW oFragColor = WR_DEBUG_OVERDRAW_COLOR; #else
new file mode 100644 --- /dev/null +++ b/gfx/wr/webrender/res/brush_conic_gradient.glsl @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define VECS_PER_CONIC_GRADIENT_BRUSH 2 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_CONIC_GRADIENT_BRUSH + +#define WR_BRUSH_VS_FUNCTION conic_gradient_brush_vs +#define WR_BRUSH_FS_FUNCTION conic_gradient_brush_fs + +#include shared,prim_shared,brush + +#define V_GRADIENT_ADDRESS flat_varying_highp_int_address_0 + +#define V_CENTER flat_varying_vec4_0.xy +#define V_ANGLE flat_varying_vec4_0.z + +// Size of the gradient pattern's rectangle, used to compute horizontal and vertical +// repetitions. Not to be confused with another kind of repetition of the pattern +// which happens along the gradient stops. +#define V_REPEATED_SIZE flat_varying_vec4_1.xy +// Repetition along the gradient stops. +#define V_GRADIENT_REPEAT flat_varying_vec4_1.z + +#define V_POS varying_vec4_0.zw + +#ifdef WR_FEATURE_ALPHA_PASS +#define V_LOCAL_POS varying_vec4_0.xy +#define V_TILE_REPEAT flat_varying_vec4_2.xy +#endif + +#define PI 3.1415926538 + +#ifdef WR_VERTEX_SHADER + +struct ConicGradient { + vec2 center_point; + float angle; + int extend_mode; + vec2 stretch_size; +}; + +ConicGradient fetch_gradient(int address) { + vec4 data[2] = fetch_from_gpu_cache_2(address); + return ConicGradient( + data[0].xy, + float(data[0].z), + int(data[1].x), + data[1].yz + ); +} + +void conic_gradient_brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize local_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 texel_rect +) { + ConicGradient gradient = fetch_gradient(prim_address); + + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + V_POS = (vi.local_pos - segment_rect.p0) / segment_rect.size; + V_POS = V_POS * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; + V_POS = V_POS * local_rect.size; + } else { + V_POS = vi.local_pos - local_rect.p0; + } + + V_CENTER = gradient.center_point; + V_ANGLE = gradient.angle; + + vec2 tile_repeat = local_rect.size / gradient.stretch_size; + V_REPEATED_SIZE = gradient.stretch_size; + + V_GRADIENT_ADDRESS = prim_user_data.x; + + // Whether to repeat the gradient along the line instead of clamping. + V_GRADIENT_REPEAT = float(gradient.extend_mode != EXTEND_MODE_CLAMP); + +#ifdef WR_FEATURE_ALPHA_PASS + V_TILE_REPEAT = tile_repeat; + V_LOCAL_POS = vi.local_pos; +#endif +} +#endif + +#ifdef WR_FRAGMENT_SHADER +Fragment conic_gradient_brush_fs() { + +#ifdef WR_FEATURE_ALPHA_PASS + // Handle top and left inflated edges (see brush_image). + vec2 local_pos = max(V_POS, vec2(0.0)); + + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(local_pos, V_REPEATED_SIZE); + + vec2 prim_size = V_REPEATED_SIZE * V_TILE_REPEAT; + // Handle bottom and right inflated edges (see brush_image). + if (local_pos.x >= prim_size.x) { + pos.x = V_REPEATED_SIZE.x; + } + if (local_pos.y >= prim_size.y) { + pos.y = V_REPEATED_SIZE.y; + } +#else + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(V_POS, V_REPEATED_SIZE); +#endif + + vec2 current_dir = pos - V_CENTER; + float current_angle = atan(current_dir.y, current_dir.x) + (PI / 2 - V_ANGLE); + float offset = mod(current_angle / (2 * PI), 1.0); + + vec4 color = sample_gradient(V_GRADIENT_ADDRESS, + offset, + V_GRADIENT_REPEAT); + +#ifdef WR_FEATURE_ALPHA_PASS + color *= init_transform_fs(V_LOCAL_POS); +#endif + + return Fragment(color); +} +#endif + +// Undef macro names that could be re-defined by other shaders. +#undef V_GRADIENT_ADDRESS +#undef V_START_POINT +#undef V_SCALE_DIR +#undef V_REPEATED_SIZE +#undef V_GRADIENT_REPEAT +#undef V_POS +#undef V_LOCAL_POS +#undef V_TILE_REPEAT
--- a/gfx/wr/webrender/res/brush_multi.glsl +++ b/gfx/wr/webrender/res/brush_multi.glsl @@ -13,20 +13,21 @@ #define WR_FEATURE_MULTI_BRUSH // These constants must match the BrushShaderKind enum in gpu_types.rs. #define BRUSH_KIND_SOLID 1 #define BRUSH_KIND_IMAGE 2 #define BRUSH_KIND_TEXT 3 #define BRUSH_KIND_LINEAR_GRADIENT 4 #define BRUSH_KIND_RADIAL_GRADIENT 5 -#define BRUSH_KIND_BLEND 6 -#define BRUSH_KIND_MIX_BLEND 7 -#define BRUSH_KIND_YV 8 -#define BRUSH_KIND_OPACITY 9 +#define BRUSH_KIND_CONIC_GRADIENT 6 +#define BRUSH_KIND_BLEND 7 +#define BRUSH_KIND_MIX_BLEND 8 +#define BRUSH_KIND_YV 9 +#define BRUSH_KIND_OPACITY 10 int vecs_per_brush(int brush_kind); #include shared,prim_shared,brush #ifdef WR_FEATURE_IMAGE_BRUSH #include brush_image #endif @@ -70,16 +71,24 @@ int vecs_per_brush(int brush_kind); #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH #include brush_radial_gradient #endif #undef VECS_PER_SPECIFIC_BRUSH #undef WR_BRUSH_VS_FUNCTION #undef WR_BRUSH_FS_FUNCTION +#ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH +#include brush_conic_gradient +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + #ifdef WR_FEATURE_OPACITY_BRUSH #include brush_opacity #endif int vecs_per_brush(int brush_kind) { switch (brush_kind) { // The default arm should never be taken, we let it point to whichever shader // is enabled first to satisfy ANGLE validation. @@ -104,16 +113,21 @@ int vecs_per_brush(int brush_kind) { #ifdef WR_FEATURE_LINEAR_GRADIENT_BRUSH case BRUSH_KIND_LINEAR_GRADIENT: return VECS_PER_LINEAR_GRADIENT_BRUSH; #endif #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH case BRUSH_KIND_RADIAL_GRADIENT: return VECS_PER_RADIAL_GRADIENT_BRUSH; #endif + + #ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH + case BRUSH_KIND_CONIC_GRADIENT: return VECS_PER_CONIC_GRADIENT_BRUSH; + #endif + #ifdef WR_FEATURE_OPACITY_BRUSH case BRUSH_KIND_OPACITY: return VECS_PER_OPACITY_BRUSH; #endif } } #define BRUSH_VS_PARAMS vi, prim_address, local_rect, segment_rect, \ prim_user_data, specific_resource_address, transform, pic_task, \ @@ -168,16 +182,22 @@ void multi_brush_vs( #endif #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH case BRUSH_KIND_RADIAL_GRADIENT: radial_gradient_brush_vs(BRUSH_VS_PARAMS); break; #endif + #ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH + case BRUSH_KIND_CONIC_GRADIENT: + conic_gradient_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + #ifdef WR_FEATURE_OPACITY_BRUSH case BRUSH_KIND_OPACITY: opacity_brush_vs(BRUSH_VS_PARAMS); break; #endif } } @@ -208,15 +228,19 @@ Fragment multi_brush_fs(int brush_kind) #ifdef WR_FEATURE_LINEAR_GRADIENT_BRUSH case BRUSH_KIND_LINEAR_GRADIENT: return linear_gradient_brush_fs(); #endif #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH case BRUSH_KIND_RADIAL_GRADIENT: return radial_gradient_brush_fs(); #endif + #ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH + case BRUSH_KIND_CONIC_GRADIENT: return conic_gradient_brush_fs(); + #endif + #ifdef WR_FEATURE_OPACITY_BRUSH case BRUSH_KIND_OPACITY: return opacity_brush_fs(); #endif } } #endif
--- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -51,16 +51,17 @@ pub enum BrushBatchKind { Image(ImageBufferKind), Blend, MixBlend { task_id: RenderTaskId, source_id: RenderTaskId, backdrop_id: RenderTaskId, }, YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange), + ConicGradient, RadialGradient, LinearGradient, Opacity, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -72,16 +73,17 @@ pub enum BatchKind { impl BatchKind { fn shader_kind(&self) -> BrushShaderKind { match self { BatchKind::Brush(BrushBatchKind::Solid) => BrushShaderKind::Solid, BatchKind::Brush(BrushBatchKind::Image(..)) => BrushShaderKind::Image, BatchKind::Brush(BrushBatchKind::LinearGradient) => BrushShaderKind::LinearGradient, BatchKind::Brush(BrushBatchKind::RadialGradient) => BrushShaderKind::RadialGradient, + BatchKind::Brush(BrushBatchKind::ConicGradient) => BrushShaderKind::ConicGradient, BatchKind::Brush(BrushBatchKind::Blend) => BrushShaderKind::Blend, BatchKind::Brush(BrushBatchKind::MixBlend { .. }) => BrushShaderKind::MixBlend, BatchKind::Brush(BrushBatchKind::YuvImage(..)) => BrushShaderKind::Yuv, BatchKind::Brush(BrushBatchKind::Opacity) => BrushShaderKind::Opacity, BatchKind::TextRun(..) => BrushShaderKind::Text, _ => BrushShaderKind::None, } } @@ -2325,16 +2327,97 @@ impl BatchBuilder { gpu_cache, &prim_header, prim_headers, z_id, prim_vis_mask, ); } } + PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => { + let prim_data = &ctx.data_stores.conic_grad[data_handle]; + let specified_blend_mode = BlendMode::PremultipliedAlpha; + + let mut prim_header = PrimitiveHeader { + local_rect: prim_rect, + local_clip_rect: prim_info.combined_local_clip_rect, + specific_prim_address: GpuCacheAddress::INVALID, + transform_id, + }; + + if visible_tiles_range.is_empty() { + let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || + prim_info.clip_task_index != ClipTaskIndex::INVALID || + transform_kind == TransformedRectKind::Complex + { + specified_blend_mode + } else { + BlendMode::None + }; + + let batch_params = BrushBatchParameters::shared( + BrushBatchKind::ConicGradient, + BatchTextures::no_texture(), + [ + prim_data.stops_handle.as_int(gpu_cache), + 0, + 0, + 0, + ], + 0, + ); + + prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); + + let prim_header_index = prim_headers.push( + &prim_header, + z_id, + batch_params.prim_user_data, + ); + + let segments = if prim_data.brush_segments.is_empty() { + None + } else { + Some(prim_data.brush_segments.as_slice()) + }; + + self.add_segmented_prim_to_batch( + segments, + prim_data.opacity, + &batch_params, + specified_blend_mode, + non_segmented_blend_mode, + batch_features, + prim_header_index, + bounding_rect, + transform_kind, + render_tasks, + z_id, + prim_info.clip_task_index, + prim_vis_mask, + ctx, + ); + } else { + let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range]; + + self.add_gradient_tiles( + visible_tiles, + &prim_data.stops_handle, + BrushBatchKind::ConicGradient, + specified_blend_mode, + bounding_rect, + clip_task_address.unwrap(), + gpu_cache, + &prim_header, + prim_headers, + z_id, + prim_vis_mask, + ); + } + } PrimitiveInstanceKind::Backdrop { data_handle } => { let prim_data = &ctx.data_stores.backdrop[data_handle]; let backdrop_pic_index = prim_data.kind.pic_index; let backdrop_surface_index = ctx.prim_store.pictures[backdrop_pic_index.0] .raster_config .as_ref() .expect("backdrop surface should be alloc by now") .surface_index;
--- a/gfx/wr/webrender/src/gpu_types.rs +++ b/gfx/wr/webrender/src/gpu_types.rs @@ -73,20 +73,21 @@ impl ZBufferIdGenerator { #[derive(Copy, Clone, Debug, PartialEq)] pub enum BrushShaderKind { None = 0, Solid = 1, Image = 2, Text = 3, LinearGradient = 4, RadialGradient = 5, - Blend = 6, - MixBlend = 7, - Yuv = 8, - Opacity = 9, + ConicGradient = 6, + Blend = 7, + MixBlend = 8, + Yuv = 9, + Opacity = 10, } #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[repr(C)] pub enum RasterizationSpace { Local = 0,
--- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -117,17 +117,17 @@ use crate::intern::{Internable, UpdateLi use crate::intern::{UpdateKind}; #[cfg(any(feature = "capture", feature = "replay"))] use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::backdrop::Backdrop; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; #[cfg(any(feature = "capture", feature = "replay"))] -use crate::prim_store::gradient::{LinearGradient, RadialGradient}; +use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::image::{Image, YuvImage}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::line_dec::LineDecoration; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::picture::Picture; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::text_run::TextRun; @@ -2709,16 +2709,17 @@ impl TileCacheInstance { } PrimitiveInstanceKind::Clear { .. } => { backdrop_candidate = Some(BackdropKind::Clear); } PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::LinearGradient { .. } | PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | PrimitiveInstanceKind::Backdrop { .. } => { // These don't contribute dependencies } }; // If this primitive considers itself a backdrop candidate, apply further // checks to see if it matches all conditions to be a backdrop. if let Some(backdrop_candidate) = backdrop_candidate {
--- a/gfx/wr/webrender/src/prim_store/gradient.rs +++ b/gfx/wr/webrender/src/prim_store/gradient.rs @@ -557,16 +557,232 @@ impl InternablePrimitive for RadialGradi impl IsVisible for RadialGradient { fn is_visible(&self) -> bool { true } } //////////////////////////////////////////////////////////////////////////////// +/// Conic gradients + +/// Hashable conic gradient parameters, for use during prim interning. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, MallocSizeOf, PartialEq)] +pub struct ConicGradientAngle { + pub angle: f32, // in radians +} + +impl Eq for ConicGradientAngle {} + +impl hash::Hash for ConicGradientAngle { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.angle.to_bits().hash(state); + } +} + +/// Identifying key for a line decoration. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] +pub struct ConicGradientKey { + pub common: PrimKeyCommonData, + pub extend_mode: ExtendMode, + pub center: PointKey, + pub angle: ConicGradientAngle, + pub stretch_size: SizeKey, + pub stops: Vec<GradientStopKey>, + pub tile_spacing: SizeKey, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl ConicGradientKey { + pub fn new( + flags: PrimitiveFlags, + prim_size: LayoutSize, + conic_grad: ConicGradient, + ) -> Self { + ConicGradientKey { + common: PrimKeyCommonData { + flags, + prim_size: prim_size.into(), + }, + extend_mode: conic_grad.extend_mode, + center: conic_grad.center, + angle: conic_grad.angle, + stretch_size: conic_grad.stretch_size, + stops: conic_grad.stops, + tile_spacing: conic_grad.tile_spacing, + nine_patch: conic_grad.nine_patch, + } + } +} + +impl InternDebug for ConicGradientKey {} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct ConicGradientTemplate { + pub common: PrimTemplateCommonData, + pub extend_mode: ExtendMode, + pub center: LayoutPoint, + pub angle: ConicGradientAngle, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub brush_segments: Vec<BrushSegment>, + pub stops: Vec<GradientStop>, + pub stops_handle: GpuCacheHandle, +} + +impl Deref for ConicGradientTemplate { + type Target = PrimTemplateCommonData; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl DerefMut for ConicGradientTemplate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.common + } +} + +impl From<ConicGradientKey> for ConicGradientTemplate { + fn from(item: ConicGradientKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(item.common); + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = item.nine_patch { + brush_segments = nine_patch.create_segments(common.prim_size); + } + + let stops = item.stops.iter().map(|stop| { + GradientStop { + offset: stop.offset, + color: stop.color.into(), + } + }).collect(); + + ConicGradientTemplate { + common, + center: item.center.into(), + extend_mode: item.extend_mode, + angle: item.angle, + stretch_size: item.stretch_size.into(), + tile_spacing: item.tile_spacing.into(), + brush_segments, + stops, + stops_handle: GpuCacheHandle::new(), + } + } +} + +impl ConicGradientTemplate { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = + frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { + // write_prim_gpu_blocks + request.push([ + self.center.x, + self.center.y, + self.angle.angle, + 0.0, + ]); + request.push([ + pack_as_float(self.extend_mode as u32), + self.stretch_size.width, + self.stretch_size.height, + 0.0, + ]); + + // write_segment_gpu_blocks + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } + + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { + GradientGpuBlockBuilder::build( + false, + &mut request, + &self.stops, + ); + } + + self.opacity = PrimitiveOpacity::translucent(); + } +} + +pub type ConicGradientDataHandle = InternHandle<ConicGradient>; + +#[derive(Debug, MallocSizeOf)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ConicGradient { + pub extend_mode: ExtendMode, + pub center: PointKey, + pub angle: ConicGradientAngle, + pub stretch_size: SizeKey, + pub stops: Vec<GradientStopKey>, + pub tile_spacing: SizeKey, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl Internable for ConicGradient { + type Key = ConicGradientKey; + type StoreData = ConicGradientTemplate; + type InternData = PrimitiveSceneData; +} + +impl InternablePrimitive for ConicGradient { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> ConicGradientKey { + ConicGradientKey::new( + info.flags, + info.rect.size, + self, + ) + } + + fn make_instance_kind( + _key: ConicGradientKey, + data_handle: ConicGradientDataHandle, + _prim_store: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::ConicGradient { + data_handle, + visible_tiles_range: GradientTileRange::empty(), + } + } +} + +impl IsVisible for ConicGradient { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + // The gradient entry index for the first color stop pub const GRADIENT_DATA_FIRST_STOP: usize = 0; // The gradient entry index for the last color stop pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1; // The start of the gradient data table pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1; // The exclusive bound of the gradient data table @@ -778,9 +994,13 @@ fn test_struct_sizes() { // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed"); assert_eq!(mem::size_of::<LinearGradientTemplate>(), 112, "LinearGradientTemplate size changed"); assert_eq!(mem::size_of::<LinearGradientKey>(), 80, "LinearGradientKey size changed"); assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed"); assert_eq!(mem::size_of::<RadialGradientTemplate>(), 120, "RadialGradientTemplate size changed"); assert_eq!(mem::size_of::<RadialGradientKey>(), 88, "RadialGradientKey size changed"); + + assert_eq!(mem::size_of::<ConicGradient>(), 64, "ConicGradient size changed"); + assert_eq!(mem::size_of::<ConicGradientTemplate>(), 112, "ConicGradientTemplate size changed"); + assert_eq!(mem::size_of::<ConicGradientKey>(), 80, "ConicGradientKey size changed"); }
--- a/gfx/wr/webrender/src/prim_store/interned.rs +++ b/gfx/wr/webrender/src/prim_store/interned.rs @@ -3,12 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // list of all interned primitives to match enumerate_interners! pub use crate::prim_store::backdrop::Backdrop; pub use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; pub use crate::prim_store::image::{Image, YuvImage}; pub use crate::prim_store::line_dec::{LineDecoration}; -pub use crate::prim_store::gradient::{LinearGradient, RadialGradient}; +pub use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient}; pub use crate::prim_store::picture::Picture; pub use crate::prim_store::text_run::TextRun;
--- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -27,17 +27,17 @@ use crate::image::{Repetition}; use crate::intern; use crate::internal_types::PlaneSplitAnchor; use malloc_size_of::MallocSizeOf; use crate::picture::{PictureCompositeMode, PicturePrimitive, ClusterFlags, TileCacheLogger}; use crate::picture::{PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles, RasterConfig}; use crate::prim_store::backdrop::BackdropDataHandle; use crate::prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle}; use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey}; -use crate::prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle}; +use crate::prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle}; use crate::prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle}; use crate::prim_store::line_dec::LineDecorationDataHandle; use crate::prim_store::picture::PictureDataHandle; use crate::prim_store::text_run::{TextRunDataHandle, TextRunPrimitive}; #[cfg(debug_assertions)] use crate::render_backend::{FrameId}; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; @@ -1421,16 +1421,21 @@ pub enum PrimitiveInstanceKind { data_handle: LinearGradientDataHandle, gradient_index: LinearGradientIndex, }, RadialGradient { /// Handle to the common interned data for this primitive. data_handle: RadialGradientDataHandle, visible_tiles_range: GradientTileRange, }, + ConicGradient { + /// Handle to the common interned data for this primitive. + data_handle: ConicGradientDataHandle, + visible_tiles_range: GradientTileRange, + }, /// Clear out a rect, used for special effects. Clear { /// Handle to the common interned data for this primitive. data_handle: PrimitiveDataHandle, }, /// Render a portion of a specified backdrop. Backdrop { data_handle: BackdropDataHandle, @@ -1618,16 +1623,19 @@ impl PrimitiveInstance { data_handle.uid() } PrimitiveInstanceKind::Picture { data_handle, .. } => { data_handle.uid() } PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { data_handle.uid() } + PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { + data_handle.uid() + } PrimitiveInstanceKind::TextRun { data_handle, .. } => { data_handle.uid() } PrimitiveInstanceKind::YuvImage { data_handle, .. } => { data_handle.uid() } PrimitiveInstanceKind::Backdrop { data_handle, .. } => { data_handle.uid() @@ -2235,16 +2243,17 @@ impl PrimitiveStore { PrimitiveInstanceKind::LineDecoration { .. } => debug_colors::PURPLE, PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } => debug_colors::ORANGE, PrimitiveInstanceKind::Rectangle { .. } => ColorF { r: 0.8, g: 0.8, b: 0.8, a: 0.5 }, PrimitiveInstanceKind::YuvImage { .. } => debug_colors::BLUE, PrimitiveInstanceKind::Image { .. } => debug_colors::BLUE, PrimitiveInstanceKind::LinearGradient { .. } => debug_colors::PINK, PrimitiveInstanceKind::RadialGradient { .. } => debug_colors::PINK, + PrimitiveInstanceKind::ConicGradient { .. } => debug_colors::PINK, PrimitiveInstanceKind::Clear { .. } => debug_colors::CYAN, PrimitiveInstanceKind::Backdrop { .. } => debug_colors::MEDIUMAQUAMARINE, }; if debug_color.a != 0.0 { let debug_rect = clipped_world_rect * frame_context.global_device_pixel_scale; frame_state.scratch.push_debug_rect(debug_rect, debug_color, debug_color.scale_alpha(0.5)); } } else if frame_context.debug_flags.contains(::api::DebugFlags::OBSCURE_IMAGES) { @@ -2554,16 +2563,17 @@ impl PrimitiveStore { } PrimitiveInstanceKind::Clear { .. } | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::LinearGradient { .. } | PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | PrimitiveInstanceKind::PushClipChain | PrimitiveInstanceKind::PopClipChain | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::Backdrop { .. } => { // These prims don't support opacity collapse } PrimitiveInstanceKind::Picture { pic_index, .. } => { let pic = &self.pictures[pic_index.0]; @@ -2697,16 +2707,17 @@ impl PrimitiveStore { PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::LinearGradient { .. } | PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | PrimitiveInstanceKind::PushClipChain | PrimitiveInstanceKind::PopClipChain | PrimitiveInstanceKind::Clear { .. } | PrimitiveInstanceKind::Backdrop { .. } => { None } } }; @@ -3322,16 +3333,80 @@ impl PrimitiveStore { if visible_tiles_range.is_empty() { prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; } } // TODO(gw): Consider whether it's worth doing segment building // for gradient primitives. } + PrimitiveInstanceKind::ConicGradient { data_handle, ref mut visible_tiles_range, .. } => { + let prim_data = &mut data_stores.conic_grad[*data_handle]; + + if prim_data.stretch_size.width >= prim_data.common.prim_size.width && + prim_data.stretch_size.height >= prim_data.common.prim_size.height { + + // We are performing the decomposition on the CPU here, no need to + // have it in the shader. + prim_data.common.may_need_repetition = false; + } + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update(frame_state); + + if prim_data.tile_spacing != LayoutSize::zero() { + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + let prim_rect = LayoutRect::new( + prim_instance.prim_origin, + prim_data.common.prim_size, + ); + + let map_local_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + prim_spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + + prim_data.common.may_need_repetition = false; + + *visible_tiles_range = decompose_repeated_primitive( + &prim_info.combined_local_clip_rect, + &prim_rect, + prim_info.clipped_world_rect, + &prim_data.stretch_size, + &prim_data.tile_spacing, + frame_state, + &mut scratch.gradient_tiles, + &map_local_to_world, + &mut |_, mut request| { + request.push([ + prim_data.center.x, + prim_data.center.y, + prim_data.angle.angle, + 0.0, + ]); + request.push([ + pack_as_float(prim_data.extend_mode as u32), + prim_data.stretch_size.width, + prim_data.stretch_size.height, + 0.0, + ]); + }, + ); + + if visible_tiles_range.is_empty() { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + + // TODO(gw): Consider whether it's worth doing segment building + // for gradient primitives. + } PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, data_handle, .. } => { let pic = &mut self.pictures[pic_index.0]; let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; data_stores.picture[*data_handle].common.may_need_repetition = false; if pic.prepare_for_render( frame_context, @@ -3713,16 +3788,17 @@ impl PrimitiveInstance { } } PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::Clear { .. } | PrimitiveInstanceKind::LinearGradient { .. } | PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | PrimitiveInstanceKind::PushClipChain | PrimitiveInstanceKind::PopClipChain | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::Backdrop { .. } => { // These primitives don't support / need segments. return; } }; @@ -3860,16 +3936,27 @@ impl PrimitiveInstance { // TODO: This is quite messy - once we remove legacy primitives we // can change this to be a tuple match on (instance, template) if prim_data.brush_segments.is_empty() { return false; } prim_data.brush_segments.as_slice() } + PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { + let prim_data = &data_stores.conic_grad[data_handle]; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + if prim_data.brush_segments.is_empty() { + return false; + } + + prim_data.brush_segments.as_slice() + } }; // If there are no segments, early out to avoid setting a valid // clip task instance location below. if segments.is_empty() { return true; }
--- a/gfx/wr/webrender/src/profiler.rs +++ b/gfx/wr/webrender/src/profiler.rs @@ -800,16 +800,17 @@ impl BackendProfileCounters { display_lists: ResourceProfileCounter::new( "Display Lists Sent", None, Some(expected::DISPLAY_LIST_MB), ), }, //TODO: generate this by a macro intern: InternProfileCounters { prim: ResourceProfileCounter::new("Interned primitives", None, None), + conic_grad: ResourceProfileCounter::new("Interned conic gradients", None, None), image: ResourceProfileCounter::new("Interned images", None, None), image_border: ResourceProfileCounter::new("Interned image borders", None, None), line_decoration: ResourceProfileCounter::new("Interned line decorations", None, None), linear_grad: ResourceProfileCounter::new("Interned linear gradients", None, None), normal_border: ResourceProfileCounter::new("Interned normal borders", None, None), picture: ResourceProfileCounter::new("Interned pictures", None, None), radial_grad: ResourceProfileCounter::new("Interned radial gradients", None, None), text_run: ResourceProfileCounter::new("Interned text runs", None, None),
--- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -304,16 +304,20 @@ impl DataStores { PrimitiveInstanceKind::Picture { data_handle, .. } => { let prim_data = &self.picture[data_handle]; &prim_data.common } PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { let prim_data = &self.radial_grad[data_handle]; &prim_data.common } + PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { + let prim_data = &self.conic_grad[data_handle]; + &prim_data.common + } PrimitiveInstanceKind::TextRun { data_handle, .. } => { let prim_data = &self.text_run[data_handle]; &prim_data.common } PrimitiveInstanceKind::YuvImage { data_handle, .. } => { let prim_data = &self.yuv_image[data_handle]; &prim_data.common }
--- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -148,16 +148,20 @@ const GPU_TAG_BRUSH_OPACITY: GpuProfileT const GPU_TAG_BRUSH_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag { label: "B_LinearGradient", color: debug_colors::POWDERBLUE, }; const GPU_TAG_BRUSH_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "B_RadialGradient", color: debug_colors::LIGHTPINK, }; +const GPU_TAG_BRUSH_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag { + label: "B_ConicGradient", + color: debug_colors::GREEN, +}; const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfileTag = GpuProfileTag { label: "B_YuvImage", color: debug_colors::DARKGREEN, }; const GPU_TAG_BRUSH_MIXBLEND: GpuProfileTag = GpuProfileTag { label: "B_MixBlend", color: debug_colors::MAGENTA, }; @@ -250,16 +254,17 @@ impl BatchKind { BatchKind::SplitComposite => "SplitComposite", BatchKind::Brush(kind) => { match kind { BrushBatchKind::Solid => "Brush (Solid)", BrushBatchKind::Image(..) => "Brush (Image)", BrushBatchKind::Blend => "Brush (Blend)", BrushBatchKind::MixBlend { .. } => "Brush (Composite)", BrushBatchKind::YuvImage(..) => "Brush (YuvImage)", + BrushBatchKind::ConicGradient => "Brush (ConicGradient)", BrushBatchKind::RadialGradient => "Brush (RadialGradient)", BrushBatchKind::LinearGradient => "Brush (LinearGradient)", BrushBatchKind::Opacity => "Brush (Opacity)", } } BatchKind::TextRun(_) => "TextRun", } } @@ -269,16 +274,17 @@ impl BatchKind { BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE, BatchKind::Brush(kind) => { match kind { BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID, BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE, BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND, BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND, BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE, + BrushBatchKind::ConicGradient => GPU_TAG_BRUSH_CONIC_GRADIENT, BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT, BrushBatchKind::LinearGradient => GPU_TAG_BRUSH_LINEAR_GRADIENT, BrushBatchKind::Opacity => GPU_TAG_BRUSH_OPACITY, } } BatchKind::TextRun(_) => GPU_TAG_PRIM_TEXT_RUN, } } @@ -6860,16 +6866,17 @@ enum FramebufferKind { Other, } fn should_skip_batch(kind: &BatchKind, flags: DebugFlags) -> bool { match kind { BatchKind::TextRun(_) => { flags.contains(DebugFlags::DISABLE_TEXT_PRIMS) } + BatchKind::Brush(BrushBatchKind::ConicGradient) | BatchKind::Brush(BrushBatchKind::RadialGradient) | BatchKind::Brush(BrushBatchKind::LinearGradient) => { flags.contains(DebugFlags::DISABLE_GRADIENT_PRIMS) } _ => false, } }
--- a/gfx/wr/webrender/src/scene_builder_thread.rs +++ b/gfx/wr/webrender/src/scene_builder_thread.rs @@ -12,17 +12,17 @@ use api::units::LayoutSize; use crate::capture::CaptureConfig; use crate::frame_builder::FrameBuilderConfig; use crate::scene_building::SceneBuilder; use crate::intern::{Internable, Interner, UpdateList}; use crate::internal_types::{FastHashMap, FastHashSet}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use crate::prim_store::backdrop::Backdrop; use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; -use crate::prim_store::gradient::{LinearGradient, RadialGradient}; +use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient}; use crate::prim_store::image::{Image, YuvImage}; use crate::prim_store::line_dec::LineDecoration; use crate::prim_store::picture::Picture; use crate::prim_store::text_run::TextRun; use crate::resource_cache::{AsyncBlobImageInfo, FontInstanceMap}; use crate::render_backend::DocumentView; use crate::renderer::{PipelineInfo, SceneBuilderHooks}; use crate::scene::{Scene, BuiltScene, SceneStats};
--- a/gfx/wr/webrender/src/scene_building.rs +++ b/gfx/wr/webrender/src/scene_building.rs @@ -26,17 +26,17 @@ use crate::picture::{BlitReason, Ordered use crate::prim_store::{PrimitiveInstance, PrimitiveSceneData}; use crate::prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore}; use crate::prim_store::{ScrollNodeAndClipChain, PictureIndex}; use crate::prim_store::{InternablePrimitive, SegmentInstanceIndex}; use crate::prim_store::{register_prim_chase_id, get_line_decoration_size}; use crate::prim_store::{SpaceSnapper}; use crate::prim_store::backdrop::Backdrop; use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; -use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams}; +use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, ConicGradientAngle}; use crate::prim_store::image::{Image, YuvImage}; use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey}; use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey}; use crate::prim_store::text_run::TextRun; use crate::render_backend::{DocumentView}; use crate::resource_cache::{FontInstanceMap, ImageRequest}; use crate::scene::{Scene, BuiltScene, SceneStats, StackingContextHelpers}; use crate::scene_builder_thread::Interners; @@ -1306,16 +1306,47 @@ impl<'a> SceneBuilder<'a> { self.add_nonshadowable_primitive( clip_and_scroll, &layout, Vec::new(), prim_key_kind, ); } + DisplayItem::ConicGradient(ref info) => { + let (layout, unsnapped_rect, clip_and_scroll) = self.process_common_properties_with_bounds( + &info.common, + &info.bounds, + apply_pipeline_clip, + ); + + let tile_size = process_repeat_size( + &layout.rect, + &unsnapped_rect, + info.tile_size, + ); + + let prim_key_kind = self.create_conic_gradient_prim( + &layout, + info.gradient.center, + info.gradient.angle, + item.gradient_stops(), + info.gradient.extend_mode, + tile_size, + info.tile_spacing, + None, + ); + + self.add_nonshadowable_primitive( + clip_and_scroll, + &layout, + Vec::new(), + prim_key_kind, + ); + } DisplayItem::BoxShadow(ref info) => { let (layout, _, clip_and_scroll) = self.process_common_properties_with_bounds( &info.common, &info.box_bounds, apply_pipeline_clip, ); self.add_box_shadow( @@ -2895,16 +2926,35 @@ impl<'a> SceneBuilder<'a> { self.add_nonshadowable_primitive( clip_and_scroll, info, Vec::new(), prim, ); } + NinePatchBorderSource::ConicGradient(gradient) => { + let prim = self.create_conic_gradient_prim( + &info, + gradient.center, + gradient.angle, + gradient_stops, + gradient.extend_mode, + LayoutSize::new(border.height as f32, border.width as f32), + LayoutSize::zero(), + Some(Box::new(nine_patch)), + ); + + self.add_nonshadowable_primitive( + clip_and_scroll, + info, + Vec::new(), + prim, + ); + } }; } BorderDetails::Normal(ref border) => { self.add_normal_border( info, border, border_item.widths, clip_and_scroll, @@ -3008,16 +3058,48 @@ impl<'a> SceneBuilder<'a> { params, stretch_size: stretch_size.into(), tile_spacing: tile_spacing.into(), nine_patch, stops, } } + pub fn create_conic_gradient_prim( + &mut self, + info: &LayoutPrimitiveInfo, + center: LayoutPoint, + angle: f32, + stops: ItemRange<GradientStop>, + extend_mode: ExtendMode, + stretch_size: LayoutSize, + mut tile_spacing: LayoutSize, + nine_patch: Option<Box<NinePatchDescriptor>>, + ) -> ConicGradient { + let mut prim_rect = info.rect; + simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); + + let stops = stops.iter().map(|stop| { + GradientStopKey { + offset: stop.offset, + color: stop.color.into(), + } + }).collect(); + + ConicGradient { + extend_mode, + center: center.into(), + angle: ConicGradientAngle { angle }, + stretch_size: stretch_size.into(), + tile_spacing: tile_spacing.into(), + nine_patch, + stops, + } + } + pub fn add_text( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayoutPrimitiveInfo, font_instance_key: &FontInstanceKey, text_color: &ColorF, glyph_range: ItemRange<GlyphInstance>, glyph_options: Option<GlyphOptions>,
--- a/gfx/wr/webrender/src/shade.rs +++ b/gfx/wr/webrender/src/shade.rs @@ -531,16 +531,17 @@ pub struct Shaders { // Brush shaders brush_solid: BrushShader, brush_image: Vec<Option<BrushShader>>, brush_fast_image: Vec<Option<BrushShader>>, brush_blend: BrushShader, brush_mix_blend: BrushShader, brush_yuv_image: Vec<Option<BrushShader>>, + brush_conic_gradient: BrushShader, brush_radial_gradient: BrushShader, brush_linear_gradient: BrushShader, brush_opacity: BrushShader, /// These are "cache clip shaders". These shaders are used to /// draw clip instances into the cached clip mask. The results /// of these shaders are also used by the primitive shaders. pub cs_clip_rectangle_slow: LazilyCompiledShader, @@ -609,16 +610,30 @@ impl Shaders { device, &[], options.precache_flags, false /* advanced blend */, false /* dual source */, use_pixel_local_storage, )?; + let brush_conic_gradient = BrushShader::new( + "brush_conic_gradient", + device, + if options.enable_dithering { + &[DITHERING_FEATURE] + } else { + &[] + }, + options.precache_flags, + false /* advanced blend */, + false /* dual source */, + use_pixel_local_storage, + )?; + let brush_radial_gradient = BrushShader::new( "brush_radial_gradient", device, if options.enable_dithering { &[DITHERING_FEATURE] } else { &[] }, @@ -899,16 +914,17 @@ impl Shaders { cs_scale, cs_svg_filter, brush_solid, brush_image, brush_fast_image, brush_blend, brush_mix_blend, brush_yuv_image, + brush_conic_gradient, brush_radial_gradient, brush_linear_gradient, brush_opacity, cs_clip_rectangle_slow, cs_clip_rectangle_fast, cs_clip_box_shadow, cs_clip_image, pls_init, @@ -948,16 +964,19 @@ impl Shaders { } } BrushBatchKind::Blend => { &mut self.brush_blend } BrushBatchKind::MixBlend { .. } => { &mut self.brush_mix_blend } + BrushBatchKind::ConicGradient => { + &mut self.brush_conic_gradient + } BrushBatchKind::RadialGradient => { &mut self.brush_radial_gradient } BrushBatchKind::LinearGradient => { &mut self.brush_linear_gradient } BrushBatchKind::YuvImage(image_buffer_kind, ..) => { let shader_index = @@ -985,16 +1004,17 @@ impl Shaders { pub fn deinit(self, device: &mut Device) { self.cs_scale.deinit(device); self.cs_blur_a8.deinit(device); self.cs_blur_rgba8.deinit(device); self.cs_svg_filter.deinit(device); self.brush_solid.deinit(device); self.brush_blend.deinit(device); self.brush_mix_blend.deinit(device); + self.brush_conic_gradient.deinit(device); self.brush_radial_gradient.deinit(device); self.brush_linear_gradient.deinit(device); self.brush_opacity.deinit(device); self.cs_clip_rectangle_slow.deinit(device); self.cs_clip_rectangle_fast.deinit(device); self.cs_clip_box_shadow.deinit(device); self.cs_clip_image.deinit(device); self.pls_init.deinit(device);
--- a/gfx/wr/webrender_api/src/api.rs +++ b/gfx/wr/webrender_api/src/api.rs @@ -1170,16 +1170,17 @@ macro_rules! enumerate_interners { prim: PrimitiveKeyKind, normal_border: NormalBorderPrim, image_border: ImageBorder, image: Image, yuv_image: YuvImage, line_decoration: LineDecoration, linear_grad: LinearGradient, radial_grad: RadialGradient, + conic_grad: ConicGradient, picture: Picture, text_run: TextRun, filter_data: FilterDataIntern, backdrop: Backdrop, } } }
--- a/gfx/wr/webrender_api/src/display_item.rs +++ b/gfx/wr/webrender_api/src/display_item.rs @@ -131,16 +131,17 @@ pub enum DisplayItem { HitTest(HitTestDisplayItem), Text(TextDisplayItem), Line(LineDisplayItem), Border(BorderDisplayItem), BoxShadow(BoxShadowDisplayItem), PushShadow(PushShadowDisplayItem), Gradient(GradientDisplayItem), RadialGradient(RadialGradientDisplayItem), + ConicGradient(ConicGradientDisplayItem), Image(ImageDisplayItem), RepeatingImage(RepeatingImageDisplayItem), YuvImage(YuvImageDisplayItem), BackdropFilter(BackdropFilterDisplayItem), // Clips Clip(ClipDisplayItem), ClipChain(ClipChainItem), @@ -178,16 +179,17 @@ pub enum DebugDisplayItem { HitTest(HitTestDisplayItem), Text(TextDisplayItem, Vec<font::GlyphInstance>), Line(LineDisplayItem), Border(BorderDisplayItem), BoxShadow(BoxShadowDisplayItem), PushShadow(PushShadowDisplayItem), Gradient(GradientDisplayItem), RadialGradient(RadialGradientDisplayItem), + ConicGradient(ConicGradientDisplayItem), Image(ImageDisplayItem), RepeatingImage(RepeatingImageDisplayItem), YuvImage(YuvImageDisplayItem), BackdropFilter(BackdropFilterDisplayItem), Clip(ClipDisplayItem, Vec<ComplexClipRegion>), ClipChain(ClipChainItem, Vec<ClipId>), @@ -439,16 +441,17 @@ pub enum RepeatMode { Space, } #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] pub enum NinePatchBorderSource { Image(ImageKey), Gradient(Gradient), RadialGradient(RadialGradient), + ConicGradient(ConicGradient), } #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct NinePatchBorder { /// Describes what to use as the 9-patch source image. If this is an image, /// it will be stretched to fill the size given by width x height. pub source: NinePatchBorderSource, @@ -628,16 +631,25 @@ pub struct GradientStop { pub struct RadialGradient { pub center: LayoutPoint, pub radius: LayoutSize, pub start_offset: f32, pub end_offset: f32, pub extend_mode: ExtendMode, } // IMPLICIT stops: Vec<GradientStop> +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ConicGradient { + pub center: LayoutPoint, + pub angle: f32, + pub start_offset: f32, + pub end_offset: f32, + pub extend_mode: ExtendMode, +} // IMPLICIT stops: Vec<GradientStop> + /// Just an abstraction for bundling up a bunch of clips into a "super clip". #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct ClipChainItem { pub id: ClipChainId, pub parent: Option<ClipChainId>, } // IMPLICIT clip_ids: Vec<ClipId> #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] @@ -647,16 +659,28 @@ pub struct RadialGradientDisplayItem { // FIXME: this should ideally just be `tile_origin` here, with the clip_rect // defining the bounds of the item. Needs non-trivial backend changes. pub bounds: LayoutRect, pub gradient: RadialGradient, pub tile_size: LayoutSize, pub tile_spacing: LayoutSize, } +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ConicGradientDisplayItem { + pub common: CommonItemProperties, + /// The area to tile the gradient over (first tile starts at origin of this rect) + // FIXME: this should ideally just be `tile_origin` here, with the clip_rect + // defining the bounds of the item. Needs non-trivial backend changes. + pub bounds: LayoutRect, + pub gradient: ConicGradient, + pub tile_size: LayoutSize, + pub tile_spacing: LayoutSize, +} + /// Renders a filtered region of its backdrop #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct BackdropFilterDisplayItem { pub common: CommonItemProperties, } // IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive> #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] @@ -1457,16 +1481,17 @@ impl DisplayItem { pub fn debug_name(&self) -> &'static str { match *self { DisplayItem::Border(..) => "border", DisplayItem::BoxShadow(..) => "box_shadow", DisplayItem::ClearRectangle(..) => "clear_rectangle", DisplayItem::HitTest(..) => "hit_test", DisplayItem::Clip(..) => "clip", DisplayItem::ClipChain(..) => "clip_chain", + DisplayItem::ConicGradient(..) => "conic_gradient", DisplayItem::Gradient(..) => "gradient", DisplayItem::Iframe(..) => "iframe", DisplayItem::Image(..) => "image", DisplayItem::RepeatingImage(..) => "repeating_image", DisplayItem::Line(..) => "line", DisplayItem::PopAllShadows => "pop_all_shadows", DisplayItem::PopReferenceFrame => "pop_reference_frame", DisplayItem::PopStackingContext => "pop_stacking_context",
--- a/gfx/wr/webrender_api/src/display_list.rs +++ b/gfx/wr/webrender_api/src/display_list.rs @@ -679,16 +679,17 @@ impl Serialize for BuiltDisplayList { Real::Line(v) => Debug::Line(v), Real::Image(v) => Debug::Image(v), Real::RepeatingImage(v) => Debug::RepeatingImage(v), Real::YuvImage(v) => Debug::YuvImage(v), Real::Border(v) => Debug::Border(v), Real::BoxShadow(v) => Debug::BoxShadow(v), Real::Gradient(v) => Debug::Gradient(v), Real::RadialGradient(v) => Debug::RadialGradient(v), + Real::ConicGradient(v) => Debug::ConicGradient(v), Real::Iframe(v) => Debug::Iframe(v), Real::PushReferenceFrame(v) => Debug::PushReferenceFrame(v), Real::PushStackingContext(v) => Debug::PushStackingContext(v), Real::PushShadow(v) => Debug::PushShadow(v), Real::BackdropFilter(v) => Debug::BackdropFilter(v), Real::PopReferenceFrame => Debug::PopReferenceFrame, Real::PopStackingContext => Debug::PopStackingContext, @@ -785,16 +786,17 @@ impl<'de> Deserialize<'de> for BuiltDisp Debug::Line(v) => Real::Line(v), Debug::Image(v) => Real::Image(v), Debug::RepeatingImage(v) => Real::RepeatingImage(v), Debug::YuvImage(v) => Real::YuvImage(v), Debug::Border(v) => Real::Border(v), Debug::BoxShadow(v) => Real::BoxShadow(v), Debug::Gradient(v) => Real::Gradient(v), Debug::RadialGradient(v) => Real::RadialGradient(v), + Debug::ConicGradient(v) => Real::ConicGradient(v), Debug::PushStackingContext(v) => Real::PushStackingContext(v), Debug::PushShadow(v) => Real::PushShadow(v), Debug::BackdropFilter(v) => Real::BackdropFilter(v), Debug::PopStackingContext => Real::PopStackingContext, Debug::PopReferenceFrame => Real::PopReferenceFrame, Debug::PopAllShadows => Real::PopAllShadows, Debug::ReuseItem(k) => Real::ReuseItem(k), @@ -1200,16 +1202,31 @@ impl DisplayListBuilder { extend_mode: di::ExtendMode, ) -> di::RadialGradient { let mut builder = GradientBuilder::with_stops(stops); let gradient = builder.radial_gradient(center, radius, extend_mode); self.push_stops(builder.stops()); gradient } + /// NOTE: gradients must be pushed in the order they're created + /// because create_gradient stores the stops in anticipation. + pub fn create_conic_gradient( + &mut self, + center: LayoutPoint, + angle: f32, + stops: Vec<di::GradientStop>, + extend_mode: di::ExtendMode, + ) -> di::ConicGradient { + let mut builder = GradientBuilder::with_stops(stops); + let gradient = builder.conic_gradient(center, angle, extend_mode); + self.push_stops(builder.stops()); + gradient + } + pub fn push_border( &mut self, common: &di::CommonItemProperties, bounds: LayoutRect, widths: LayoutSideOffsets, details: di::BorderDetails, ) { let item = di::DisplayItem::Border(di::BorderDisplayItem { @@ -1297,16 +1314,38 @@ impl DisplayListBuilder { gradient, tile_size, tile_spacing, }); self.push_item(&item); } + /// Pushes a conic gradient to be displayed. + /// + /// See [`push_gradient`](#method.push_gradient) for explanation. + pub fn push_conic_gradient( + &mut self, + common: &di::CommonItemProperties, + bounds: LayoutRect, + gradient: di::ConicGradient, + tile_size: LayoutSize, + tile_spacing: LayoutSize, + ) { + let item = di::DisplayItem::ConicGradient(di::ConicGradientDisplayItem { + common: *common, + bounds, + gradient, + tile_size, + tile_spacing, + }); + + self.push_item(&item); + } + pub fn push_reference_frame( &mut self, origin: LayoutPoint, parent_spatial_id: di::SpatialId, transform_style: di::TransformStyle, transform: PropertyBinding<LayoutTransform>, kind: di::ReferenceFrameKind, ) -> di::SpatialId {
--- a/gfx/wr/webrender_api/src/gradient_builder.rs +++ b/gfx/wr/webrender_api/src/gradient_builder.rs @@ -94,16 +94,37 @@ impl GradientBuilder { center, radius, start_offset, end_offset, extend_mode, } } + /// Produce a conic gradient, normalize the stops. + pub fn conic_gradient( + &mut self, + center: LayoutPoint, + angle: f32, + extend_mode: di::ExtendMode, + ) -> di::ConicGradient { + // XXX(ntim): Possibly handle negative angles ? + + let (start_offset, end_offset) = + self.normalize(extend_mode); + + di::ConicGradient { + center, + angle, + start_offset, + end_offset, + extend_mode, + } + } + /// Gradients can be defined with stops outside the range of [0, 1] /// when this happens the gradient needs to be normalized by adjusting /// the gradient stops and gradient line into an equivalent gradient /// with stops in the range [0, 1]. this is done by moving the beginning /// of the gradient line to where stop[0] and the end of the gradient line /// to stop[n-1]. this function adjusts the stops in place, and returns /// the amount to adjust the gradient line start and stop. fn normalize(&mut self, extend_mode: di::ExtendMode) -> (f32, f32) {
new file mode 100644 --- /dev/null +++ b/gfx/wr/wrench/reftests/gradient/conic-color-wheel.yaml @@ -0,0 +1,14 @@ +--- +root: + items: + - type: clip + bounds: [50, 50, 300, 300] + complex: + - rect: [50, 50, 300, 300] + radius: 300 + items: + - type: conic-gradient + bounds: 50 50 300 300 + center: 150 150 + angle: 0.0 + stops: [0.0, red, 0.16666, yellow, 0.33333, green, 0.5, [0,255,255,1], 0.66666, blue, 0.83333, [255,0,255,1], 1.0, red]
new file mode 100644 --- /dev/null +++ b/gfx/wr/wrench/reftests/gradient/conic-simple.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: conic-gradient + bounds: 50 50 300 300 + center: 150 150 + angle: 0.0 + stops: [0.0, red, 1.0, yellow] \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/gfx/wr/wrench/reftests/gradient/conic-start-angle.yaml @@ -0,0 +1,8 @@ +--- +root: + items: + - type: conic-gradient + bounds: 50 50 300 300 + center: 150 150 + angle: 0.7853981633974483 + stops: [0.0, red, 1.0, yellow] \ No newline at end of file
--- a/gfx/wr/wrench/src/yaml_frame_reader.rs +++ b/gfx/wr/wrench/src/yaml_frame_reader.rs @@ -920,16 +920,43 @@ impl YamlFrameReader { ExtendMode::Repeat } else { ExtendMode::Clamp }; dl.create_radial_gradient(center, radius, stops, extend_mode) } + fn to_conic_gradient(&mut self, dl: &mut DisplayListBuilder, item: &Yaml) -> ConicGradient { + let center = item["center"].as_point().expect("conic gradient must have center"); + let angle = item["angle"].as_force_f32().expect("conic gradient must have an angle"); + let stops = item["stops"] + .as_vec() + .expect("conic gradient must have stops") + .chunks(2) + .map(|chunk| { + GradientStop { + offset: chunk[0] + .as_force_f32() + .expect("gradient stop offset is not f32"), + color: chunk[1] + .as_colorf() + .expect("gradient stop color is not color"), + } + }) + .collect::<Vec<_>>(); + let extend_mode = if item["repeat"].as_bool().unwrap_or(false) { + ExtendMode::Repeat + } else { + ExtendMode::Clamp + }; + + dl.create_conic_gradient(center, angle, stops, extend_mode) + } + fn handle_rect( &mut self, dl: &mut DisplayListBuilder, item: &Yaml, info: &mut CommonItemProperties, ) { let bounds_key = if item["type"].is_badvalue() { "rect" @@ -1085,16 +1112,43 @@ impl YamlFrameReader { &info, bounds, gradient, tile_size, tile_spacing, ); } + fn handle_conic_gradient( + &mut self, + dl: &mut DisplayListBuilder, + item: &Yaml, + info: &mut CommonItemProperties, + ) { + let bounds_key = if item["type"].is_badvalue() { + "conic-gradient" + } else { + "bounds" + }; + let bounds = item[bounds_key] + .as_rect() + .expect("conic gradient must have bounds"); + let gradient = self.to_conic_gradient(dl, item); + let tile_size = item["tile-size"].as_size().unwrap_or(bounds.size); + let tile_spacing = item["tile-spacing"].as_size().unwrap_or(LayoutSize::zero()); + + dl.push_conic_gradient( + &info, + bounds, + gradient, + tile_size, + tile_spacing, + ); + } + fn handle_border( &mut self, dl: &mut DisplayListBuilder, wrench: &mut Wrench, item: &Yaml, info: &mut CommonItemProperties, ) { let bounds_key = if item["type"].is_badvalue() { @@ -1163,17 +1217,17 @@ impl YamlFrameReader { top, left, bottom, right, radius, do_aa, })) } - "image" | "gradient" | "radial-gradient" => { + "image" | "gradient" | "radial-gradient" | "conic-gradient" => { let image_width = item["image-width"] .as_i64() .unwrap_or(bounds.size.width as i64); let image_height = item["image-height"] .as_i64() .unwrap_or(bounds.size.height as i64); let fill = item["fill"].as_bool().unwrap_or(false); @@ -1216,17 +1270,20 @@ impl YamlFrameReader { } "gradient" => { let gradient = self.to_gradient(dl, item); NinePatchBorderSource::Gradient(gradient) } "radial-gradient" => { let gradient = self.to_radial_gradient(dl, item); NinePatchBorderSource::RadialGradient(gradient) - + } + "conic-gradient" => { + let gradient = self.to_conic_gradient(dl, item); + NinePatchBorderSource::ConicGradient(gradient) } _ => unreachable!("Unexpected border type"), }; Some(BorderDetails::NinePatch(NinePatchBorder { source, width: image_width as i32, height: image_height as i32, @@ -1588,16 +1645,17 @@ impl YamlFrameReader { "rect", "image", "text", "glyphs", "box-shadow", // Note: box_shadow shorthand check has to come before border. "border", "gradient", "radial-gradient", + "conic-gradient" ]; for shorthand in shorthands.iter() { if !item[*shorthand].is_badvalue() { return shorthand; } } item["type"].as_str().unwrap_or("unknown") @@ -1694,16 +1752,17 @@ impl YamlFrameReader { "text" | "glyphs" => self.handle_text(dl, wrench, item, &mut info), "scroll-frame" => self.handle_scroll_frame(dl, wrench, item), "sticky-frame" => self.handle_sticky_frame(dl, wrench, item), "clip" => self.handle_clip(dl, wrench, item), "clip-chain" => self.handle_clip_chain(dl, item), "border" => self.handle_border(dl, wrench, item, &mut info), "gradient" => self.handle_gradient(dl, item, &mut info), "radial-gradient" => self.handle_radial_gradient(dl, item, &mut info), + "conic-gradient" => self.handle_conic_gradient(dl, item, &mut info), "box-shadow" => self.handle_box_shadow(dl, item, &mut info), "iframe" => self.handle_iframe(dl, item, &mut info), "stacking-context" => { self.add_stacking_context_from_yaml(dl, wrench, item, false, &mut info) } "reference-frame" => self.handle_reference_frame(dl, wrench, item), "shadow" => self.handle_push_shadow(dl, item, &mut info), "pop-all-shadows" => self.handle_pop_all_shadows(dl),
--- a/gfx/wr/wrench/src/yaml_frame_writer.rs +++ b/gfx/wr/wrench/src/yaml_frame_writer.rs @@ -521,16 +521,39 @@ fn radial_gradient_to_yaml( let denormalized_stop = (stop.offset * stops_delta) + first_offset; denormalized_stops.push(Yaml::Real(denormalized_stop.to_string())); denormalized_stops.push(Yaml::String(color_to_string(stop.color))); } yaml_node(table, "stops", Yaml::Array(denormalized_stops)); bool_node(table, "repeat", gradient.extend_mode == ExtendMode::Repeat); } +fn conic_gradient_to_yaml( + table: &mut Table, + gradient: &webrender::api::ConicGradient, + stops_range: ItemRange<GradientStop>, +) { + point_node(table, "center", &gradient.center); + f32_node(table, "angle", gradient.angle); + + let first_offset = gradient.start_offset; + let last_offset = gradient.end_offset; + let stops_delta = last_offset - first_offset; + assert!(first_offset <= last_offset); + + let mut denormalized_stops = vec![]; + for stop in stops_range { + let denormalized_stop = (stop.offset * stops_delta) + first_offset; + denormalized_stops.push(Yaml::Real(denormalized_stop.to_string())); + denormalized_stops.push(Yaml::String(color_to_string(stop.color))); + } + yaml_node(table, "stops", Yaml::Array(denormalized_stops)); + bool_node(table, "repeat", gradient.extend_mode == ExtendMode::Repeat); +} + enum CachedFont { Native(NativeFontHandle, Option<PathBuf>), Raw(Option<Vec<u8>>, u32, Option<PathBuf>), } struct CachedFontInstance { font_key: FontKey, glyph_size: Au, @@ -1186,16 +1209,24 @@ impl YamlFrameWriter { NinePatchBorderSource::RadialGradient(gradient) => { str_node(&mut v, "border-type", "radial-gradient"); radial_gradient_to_yaml( &mut v, &gradient, base.gradient_stops(), ); } + NinePatchBorderSource::ConicGradient(gradient) => { + str_node(&mut v, "border-type", "conic-gradient"); + conic_gradient_to_yaml( + &mut v, + &gradient, + base.gradient_stops(), + ); + } } i32_node(&mut v, "image-width", details.width); i32_node(&mut v, "image-height", details.height); let slice = [ details.slice.top, details.slice.right, details.slice.bottom, @@ -1268,16 +1299,28 @@ impl YamlFrameWriter { size_node(&mut v, "tile-size", &item.tile_size); size_node(&mut v, "tile-spacing", &item.tile_spacing); radial_gradient_to_yaml( &mut v, &item.gradient, base.gradient_stops(), ); } + DisplayItem::ConicGradient(item) => { + str_node(&mut v, "type", "conic-gradient"); + rect_node(&mut v, "bounds", &item.bounds); + common_node(&mut v, clip_id_mapper, &item.common); + size_node(&mut v, "tile-size", &item.tile_size); + size_node(&mut v, "tile-spacing", &item.tile_spacing); + conic_gradient_to_yaml( + &mut v, + &item.gradient, + base.gradient_stops(), + ); + } DisplayItem::Iframe(item) => { str_node(&mut v, "type", "iframe"); rect_node(&mut v, "bounds", &item.bounds); rect_node(&mut v, "clip_rect", &item.clip_rect); clip_and_scroll_node( &mut v, clip_id_mapper, item.space_and_clip.clip_id,