From 8934b4bd6eaa64403bc5ba40223456e63cc2aa66 Mon Sep 17 00:00:00 2001 From: Iulian Moraru Date: Tue, 2 Apr 2024 21:57:31 +0300 Subject: [PATCH] Backed out 5 changesets (bug 1888400, bug 1887837, bug 1885571) for causing reftest failures on radial-size-1a.html. CLOSED TREE Backed out changeset b8e93a704beb (bug 1888400) Backed out changeset b15f6d439444 (bug 1887837) Backed out changeset 09548e11b44f (bug 1885571) Backed out changeset 0fd0f35dc653 (bug 1888400) Backed out changeset d9e0b8db1532 (bug 1888400) --- .../webrender/res/ps_quad_conic_gradient.glsl | 74 ------ .../res/ps_quad_radial_gradient.glsl | 81 ------ gfx/wr/webrender/src/gpu_types.rs | 1 - gfx/wr/webrender/src/pattern.rs | 6 +- gfx/wr/webrender/src/prepare.rs | 116 +-------- .../src/prim_store/gradient/conic.rs | 47 +--- .../src/prim_store/gradient/radial.rs | 48 +--- gfx/wr/webrender/src/prim_store/mod.rs | 2 - gfx/wr/webrender/src/quad.rs | 235 ++++++------------ gfx/wr/webrender/src/render_target.rs | 2 +- gfx/wr/webrender/src/renderer/mod.rs | 10 +- gfx/wr/webrender/src/renderer/shade.rs | 34 --- gfx/wr/webrender_build/src/shader_features.rs | 4 - .../radial-border-radius-large-ref.png | Bin 17159 -> 0 bytes .../gradient/radial-border-radius-large.yaml | 21 -- gfx/wr/wrench/reftests/gradient/reftest.list | 3 +- 16 files changed, 98 insertions(+), 586 deletions(-) delete mode 100644 gfx/wr/webrender/res/ps_quad_conic_gradient.glsl delete mode 100644 gfx/wr/webrender/res/ps_quad_radial_gradient.glsl delete mode 100644 gfx/wr/wrench/reftests/gradient/radial-border-radius-large-ref.png delete mode 100644 gfx/wr/wrench/reftests/gradient/radial-border-radius-large.yaml diff --git a/gfx/wr/webrender/res/ps_quad_conic_gradient.glsl b/gfx/wr/webrender/res/ps_quad_conic_gradient.glsl deleted file mode 100644 index e9a1bec2b338..000000000000 --- a/gfx/wr/webrender/res/ps_quad_conic_gradient.glsl +++ /dev/null @@ -1,74 +0,0 @@ -/* 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/. */ - -/// This shader renders radial graidents in a color or alpha target. - -#include ps_quad,gradient - -#define PI 3.141592653589793 - -// x: start offset, y: offset scale, z: angle -// Packed in to a vector to work around bug 1630356. -flat varying highp vec3 v_start_offset_offset_scale_angle_vec; -#define v_start_offset v_start_offset_offset_scale_angle_vec.x -#define v_offset_scale v_start_offset_offset_scale_angle_vec.y -#define v_angle v_start_offset_offset_scale_angle_vec.z - -varying highp vec2 v_dir; - -#ifdef WR_VERTEX_SHADER -struct ConicGradient { - vec2 center; - vec2 scale; - float start_offset; - float end_offset; - float angle; - // 1.0 if the gradient should be repeated, 0.0 otherwise. - float repeat; -}; - -ConicGradient fetch_conic_gradient(int address) { - vec4[2] data = fetch_from_gpu_buffer_2f(address); - - return ConicGradient( - data[0].xy, - data[0].zw, - data[1].x, - data[1].y, - data[1].z, - data[1].w - ); -} - -void pattern_vertex(PrimitiveInfo info) { - ConicGradient gradient = fetch_conic_gradient(info.pattern_input.x); - v_gradient_address.x = info.pattern_input.y; - v_gradient_repeat.x = gradient.repeat; - - // Store 1/d where d = end_offset - start_offset - // If d = 0, we can't get its reciprocal. Instead, just use a zero scale. - float d = gradient.end_offset - gradient.start_offset; - v_offset_scale = d != 0.0 ? 1.0 / d : 0.0; - - v_angle = PI / 2.0 - gradient.angle; - v_start_offset = gradient.start_offset * v_offset_scale; - v_dir = ((info.local_pos - info.local_prim_rect.p0) * gradient.scale - gradient.center); -} - -#endif - - -#ifdef WR_FRAGMENT_SHADER - -vec4 pattern_fragment(vec4 color) { - // Use inverse trig to find the angle offset from the relative position. - vec2 current_dir = v_dir; - float current_angle = atan(current_dir.y, current_dir.x) + v_angle; - float offset = fract(current_angle / (2.0 * PI)) * v_offset_scale - v_start_offset; - - color *= sample_gradient(offset); - return color; -} - -#endif diff --git a/gfx/wr/webrender/res/ps_quad_radial_gradient.glsl b/gfx/wr/webrender/res/ps_quad_radial_gradient.glsl deleted file mode 100644 index 05b4dd2aa8c6..000000000000 --- a/gfx/wr/webrender/res/ps_quad_radial_gradient.glsl +++ /dev/null @@ -1,81 +0,0 @@ -/* 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/. */ - -/// This shader renders radial graidents in a color or alpha target. - -#include ps_quad,gradient - -// Start radius. Packed in to a vector to work around bug 1630356. -flat varying highp vec2 v_start_radius; -varying highp vec2 v_pos; - -struct RadialGradient { - vec2 center; - vec2 scale; - float start_radius; - float end_radius; - float xy_ratio; - // 1.0 if the gradient should be repeated, 0.0 otherwise. - float repeat; -}; - -RadialGradient fetch_radial_gradient(int address) { - vec4[2] data = fetch_from_gpu_buffer_2f(address); - - return RadialGradient( - data[0].xy, - data[0].zw, - data[1].x, - data[1].y, - data[1].z, - data[1].w - ); -} - -#ifdef WR_VERTEX_SHADER -void pattern_vertex(PrimitiveInfo info) { - RadialGradient gradient = fetch_radial_gradient(info.pattern_input.x); - v_gradient_address.x = info.pattern_input.y; - - // Store 1/rd where rd = end_radius - start_radius - // If rd = 0, we can't get its reciprocal. Instead, just use a zero scale. - float rd = gradient.end_radius - gradient.start_radius; - float radius_scale = rd != 0.0 ? 1.0 / rd : 0.0; - - v_start_radius.x = gradient.start_radius * radius_scale; - - // Transform all coordinates by the y scale so the - // fragment shader can work with circles - - // v_pos is in a coordinate space relative to the task rect - // (so it is independent of the task origin). - v_pos = ((info.local_pos - info.local_prim_rect.p0) * gradient.scale - gradient.center) * radius_scale; - v_pos.y *= gradient.xy_ratio; - - v_gradient_repeat.x = gradient.repeat; -} -#endif - -#ifdef WR_FRAGMENT_SHADER -vec4 pattern_fragment(vec4 color) { - // Solve for t in length(pd) = v_start_radius + t * rd - float offset = length(v_pos) - v_start_radius.x; - color *= sample_gradient(offset); - - return color; -} - -#if defined(SWGL_DRAW_SPAN) -void swgl_drawSpanRGBA8() { - int address = swgl_validateGradient(sGpuBufferF, get_gpu_buffer_uv(v_gradient_address.x), - int(GRADIENT_ENTRIES + 2.0)); - if (address < 0) { - return; - } - swgl_commitRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, - v_pos, v_start_radius.x); -} -#endif - -#endif diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs index fb33d3ec5cb3..e222ebed048b 100644 --- a/gfx/wr/webrender/src/gpu_types.rs +++ b/gfx/wr/webrender/src/gpu_types.rs @@ -571,7 +571,6 @@ impl From for PrimitiveInstanceData { } } -#[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] pub struct QuadSegment { pub rect: LayoutRect, diff --git a/gfx/wr/webrender/src/pattern.rs b/gfx/wr/webrender/src/pattern.rs index 87a702fb5f56..36a06fa2b992 100644 --- a/gfx/wr/webrender/src/pattern.rs +++ b/gfx/wr/webrender/src/pattern.rs @@ -10,14 +10,12 @@ use api::{ColorF, PremultipliedColorF}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum PatternKind { ColorOrTexture = 0, - RadialGradient = 1, - ConicGradient = 2, - Mask = 3, + Mask = 1, // When adding patterns, don't forget to update the NUM_PATTERNS constant. } -pub const NUM_PATTERNS: u32 = 4; +pub const NUM_PATTERNS: u32 = 2; impl PatternKind { pub fn from_u32(val: u32) -> Self { diff --git a/gfx/wr/webrender/src/prepare.rs b/gfx/wr/webrender/src/prepare.rs index ef26c74d0660..919602b3439a 100644 --- a/gfx/wr/webrender/src/prepare.rs +++ b/gfx/wr/webrender/src/prepare.rs @@ -28,7 +28,7 @@ use crate::prim_store::line_dec::MAX_LINE_DECORATION_RESOLUTION; use crate::prim_store::*; use crate::quad; use crate::pattern::Pattern; -use crate::prim_store::gradient::{radial_gradient_pattern, conic_gradient_pattern, GradientGpuBlockBuilder}; +use crate::prim_store::gradient::GradientGpuBlockBuilder; use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; use crate::render_task_cache::RenderTaskCacheKeyKind; @@ -209,32 +209,6 @@ fn prepare_prim_for_render( let prim_instance = &mut prim_instances[prim_instance_index]; if !is_passthrough { - fn may_need_repetition(stretch_size: LayoutSize, prim_rect: LayoutRect) -> bool { - stretch_size.width < prim_rect.width() || - stretch_size.height < prim_rect.height() - } - // Bug 1887841: At the moment the quad shader does not support repetitions. - // Bug 1888349: Some primitives have brush segments that aren't handled by - // the quad infrastructure yet. - let disable_quad_path = match &prim_instance.kind { - PrimitiveInstanceKind::Rectangle { .. } => false, - PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { - let prim_data = &data_stores.linear_grad[*data_handle]; - !prim_data.brush_segments.is_empty() || - may_need_repetition(prim_data.stretch_size, prim_data.common.prim_rect) - } - PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { - let prim_data = &data_stores.radial_grad[*data_handle]; - !prim_data.brush_segments.is_empty() || - may_need_repetition(prim_data.stretch_size, prim_data.common.prim_rect) - } - PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { - let prim_data = &data_stores.conic_grad[*data_handle]; - !prim_data.brush_segments.is_empty() || - may_need_repetition(prim_data.stretch_size, prim_data.common.prim_rect) - } - _ => true, - }; // In this initial patch, we only support non-masked primitives through the new // quad rendering path. Follow up patches will extend this to support masks, and @@ -242,19 +216,18 @@ fn prepare_prim_for_render( // to skip the entry point to `update_clip_task` as that does old-style segmenting // and mask generation. let should_update_clip_task = match prim_instance.kind { - PrimitiveInstanceKind::Rectangle { use_legacy_path: ref mut no_quads, .. } - | PrimitiveInstanceKind::RadialGradient { cached: ref mut no_quads, .. } - | PrimitiveInstanceKind::ConicGradient { cached: ref mut no_quads, .. } - => { - *no_quads = disable_quad_path || !can_use_clip_chain_for_quad_path( + PrimitiveInstanceKind::Rectangle { ref mut use_legacy_path, .. } => { + *use_legacy_path = !can_use_clip_chain_for_quad_path( &prim_instance.vis.clip_chain, frame_state.clip_store, data_stores, ); - *no_quads + *use_legacy_path + } + PrimitiveInstanceKind::Picture { .. } => { + false } - PrimitiveInstanceKind::Picture { .. } => false, _ => true, }; @@ -805,45 +778,12 @@ fn prepare_interned_prim_for_render( } } } - PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, cached, .. } => { + PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, .. } => { profile_scope!("RadialGradient"); let prim_data = &mut data_stores.radial_grad[*data_handle]; - if !*cached { - // The scaling parameter is used to compensate for when we reduce the size - // of the render task for cached gradients. Here we aren't applying any. - let no_scale = DeviceVector2D::one(); - - let pattern = radial_gradient_pattern( - prim_data.center, - no_scale, - &prim_data.params, - prim_data.extend_mode, - &prim_data.stops, - &mut frame_state.frame_gpu_data, - ); - - quad::push_quad( - &pattern, - &prim_data.common.prim_rect, - prim_instance_index, - prim_spatial_node_index, - &prim_instance.vis.clip_chain, - device_pixel_scale, - frame_context, - pic_context, - targets, - &data_stores.clip, - frame_state, - pic_state, - scratch, - ); - - return; - } - prim_data.common.may_need_repetition = prim_data.stretch_size.width < prim_data.common.prim_rect.width() - || prim_data.stretch_size.height < prim_data.common.prim_rect.height(); + || prim_data.stretch_size.height < prim_data.common.prim_rect.height(); // Update the template this instane references, which may refresh the GPU // cache with any shared template data. @@ -868,44 +808,14 @@ fn prepare_interned_prim_for_render( prim_instance.clear_visibility(); } } + + // TODO(gw): Consider whether it's worth doing segment building + // for gradient primitives. } - PrimitiveInstanceKind::ConicGradient { data_handle, ref mut visible_tiles_range, cached, .. } => { + PrimitiveInstanceKind::ConicGradient { data_handle, ref mut visible_tiles_range, .. } => { profile_scope!("ConicGradient"); let prim_data = &mut data_stores.conic_grad[*data_handle]; - if !*cached { - // The scaling parameter is used to compensate for when we reduce the size - // of the render task for cached gradients. Here we aren't applying any. - let no_scale = DeviceVector2D::one(); - - let pattern = conic_gradient_pattern( - prim_data.center, - no_scale, - &prim_data.params, - prim_data.extend_mode, - &prim_data.stops, - &mut frame_state.frame_gpu_data, - ); - - quad::push_quad( - &pattern, - &prim_data.common.prim_rect, - prim_instance_index, - prim_spatial_node_index, - &prim_instance.vis.clip_chain, - device_pixel_scale, - frame_context, - pic_context, - targets, - &data_stores.clip, - frame_state, - pic_state, - scratch, - ); - - return; - } - prim_data.common.may_need_repetition = prim_data.stretch_size.width < prim_data.common.prim_rect.width() || prim_data.stretch_size.height < prim_data.common.prim_rect.height(); diff --git a/gfx/wr/webrender/src/prim_store/gradient/conic.rs b/gfx/wr/webrender/src/prim_store/gradient/conic.rs index e2d69e76664b..2c4818095e34 100644 --- a/gfx/wr/webrender/src/prim_store/gradient/conic.rs +++ b/gfx/wr/webrender/src/prim_store/gradient/conic.rs @@ -11,7 +11,6 @@ use euclid::vec2; use api::{ExtendMode, GradientStop, PremultipliedColorF}; use api::units::*; -use crate::pattern::{Pattern, PatternKind, PatternShaderInput}; use crate::scene_building::IsVisible; use crate::frame_builder::FrameBuildingState; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; @@ -23,8 +22,8 @@ use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimit use crate::render_task::{RenderTask, RenderTaskKind}; use crate::render_task_graph::RenderTaskId; use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent}; -use crate::renderer::{GpuBufferAddress, GpuBufferBuilder}; -use crate::picture::SurfaceIndex; +use crate::renderer::GpuBufferAddress; +use crate::picture::{SurfaceIndex}; use std::{hash, ops::{Deref, DerefMut}}; use super::{stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder}; @@ -330,7 +329,6 @@ impl InternablePrimitive for ConicGradient { PrimitiveInstanceKind::ConicGradient { data_handle, visible_tiles_range: GradientTileRange::empty(), - cached: true, } } } @@ -399,44 +397,3 @@ pub struct ConicGradientCacheKey { pub stops: Vec, } -pub fn conic_gradient_pattern( - center: DevicePoint, - scale: DeviceVector2D, - params: &ConicGradientParams, - extend_mode: ExtendMode, - stops: &[GradientStop], - gpu_buffer_builder: &mut GpuBufferBuilder -) -> Pattern { - let mut writer = gpu_buffer_builder.f32.write_blocks(2); - writer.push_one([ - center.x, - center.y, - scale.x, - scale.y, - ]); - writer.push_one([ - params.start_offset, - params.end_offset, - params.angle, - if extend_mode == ExtendMode::Repeat { 1.0 } else { 0.0 } - ]); - let gradient_address = writer.finish(); - - let stops_address = GradientGpuBlockBuilder::build( - false, - &mut gpu_buffer_builder.f32, - &stops, - ); - - let is_opaque = stops.iter().all(|stop| stop.color.a >= 1.0); - - Pattern { - kind: PatternKind::ConicGradient, - shader_input: PatternShaderInput( - gradient_address.as_int(), - stops_address.as_int(), - ), - base_color: PremultipliedColorF::WHITE, - is_opaque, - } -} \ No newline at end of file diff --git a/gfx/wr/webrender/src/prim_store/gradient/radial.rs b/gfx/wr/webrender/src/prim_store/gradient/radial.rs index a9d62ce23888..4d91b2863398 100644 --- a/gfx/wr/webrender/src/prim_store/gradient/radial.rs +++ b/gfx/wr/webrender/src/prim_store/gradient/radial.rs @@ -11,7 +11,6 @@ use euclid::{vec2, size2}; use api::{ExtendMode, GradientStop, PremultipliedColorF, ColorU}; use api::units::*; -use crate::pattern::{Pattern, PatternKind, PatternShaderInput}; use crate::scene_building::IsVisible; use crate::frame_builder::FrameBuildingState; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; @@ -23,8 +22,8 @@ use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, FloatKey}; use crate::render_task::{RenderTask, RenderTaskKind}; use crate::render_task_graph::RenderTaskId; use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent}; -use crate::renderer::{GpuBufferAddress, GpuBufferBuilder}; -use crate::picture::SurfaceIndex; +use crate::renderer::GpuBufferAddress; +use crate::picture::{SurfaceIndex}; use std::{hash, ops::{Deref, DerefMut}}; use super::{ @@ -296,7 +295,6 @@ impl InternablePrimitive for RadialGradient { PrimitiveInstanceKind::RadialGradient { data_handle, visible_tiles_range: GradientTileRange::empty(), - cached: true, } } } @@ -531,45 +529,3 @@ pub fn optimize_radial_gradient( tile_spacing.width += l + r; tile_spacing.height += t + b; } - -pub fn radial_gradient_pattern( - center: DevicePoint, - scale: DeviceVector2D, - params: &RadialGradientParams, - extend_mode: ExtendMode, - stops: &[GradientStop], - gpu_buffer_builder: &mut GpuBufferBuilder -) -> Pattern { - let mut writer = gpu_buffer_builder.f32.write_blocks(2); - writer.push_one([ - center.x, - center.y, - scale.x, - scale.y, - ]); - writer.push_one([ - params.start_radius, - params.end_radius, - params.ratio_xy, - if extend_mode == ExtendMode::Repeat { 1.0 } else { 0.0 } - ]); - let gradient_address = writer.finish(); - - let stops_address = GradientGpuBlockBuilder::build( - false, - &mut gpu_buffer_builder.f32, - &stops, - ); - - let is_opaque = stops.iter().all(|stop| stop.color.a >= 1.0); - - Pattern { - kind: PatternKind::RadialGradient, - shader_input: PatternShaderInput( - gradient_address.as_int(), - stops_address.as_int(), - ), - base_color: PremultipliedColorF::WHITE, - is_opaque, - } -} \ No newline at end of file diff --git a/gfx/wr/webrender/src/prim_store/mod.rs b/gfx/wr/webrender/src/prim_store/mod.rs index 6101fb00d92e..cc09eab6b14e 100644 --- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -1029,13 +1029,11 @@ pub enum PrimitiveInstanceKind { /// Handle to the common interned data for this primitive. data_handle: RadialGradientDataHandle, visible_tiles_range: GradientTileRange, - cached: bool, }, ConicGradient { /// Handle to the common interned data for this primitive. data_handle: ConicGradientDataHandle, visible_tiles_range: GradientTileRange, - cached: bool, }, /// Clear out a rect, used for special effects. Clear { diff --git a/gfx/wr/webrender/src/quad.rs b/gfx/wr/webrender/src/quad.rs index 7de3ed8542d4..9db434711b09 100644 --- a/gfx/wr/webrender/src/quad.rs +++ b/gfx/wr/webrender/src/quad.rs @@ -162,10 +162,10 @@ pub fn push_quad( match strategy { QuadRenderStrategy::Direct => {} QuadRenderStrategy::Indirect => { - let task_id = add_render_task_with_mask( + let segment = add_segment( pattern, - clipped_surface_rect.size(), - clipped_surface_rect.min.to_f32(), + &clipped_surface_rect, + true, clip_chain, prim_spatial_node_index, pic_context.raster_spatial_node_index, @@ -178,15 +178,14 @@ pub fn push_quad( frame_state, ); - let rect = clipped_surface_rect.to_f32().cast_unit(); add_composite_prim( pattern, prim_instance_index, - rect, + segment.rect, quad_flags, frame_state, targets, - &[QuadSegment { rect, task_id }], + &[segment], ); } QuadRenderStrategy::Tiled { x_tiles, y_tiles } => { @@ -227,17 +226,16 @@ pub fn push_quad( continue; } - let int_rect = DeviceIntRect { + let create_task = true; + let rect = DeviceIntRect { min: point2(x0, y0), max: point2(x1, y1), }; - let rect = int_rect.to_f32(); - - let task_id = add_render_task_with_mask( + let segment = add_segment( pattern, - int_rect.size(), - rect.min, + &rect, + create_task, clip_chain, prim_spatial_node_index, pic_context.raster_spatial_node_index, @@ -249,8 +247,7 @@ pub fn push_quad( needs_scissor, frame_state, ); - - scratch.quad_segments.push(QuadSegment { rect: rect.cast_unit(), task_id }); + scratch.quad_segments.push(segment); } } @@ -321,16 +318,6 @@ pub fn push_quad( scratch.quad_segments.clear(); - fn should_create_task(mode: ClipMode, x: usize, y: usize) -> bool { - match mode { - // Only create render tasks for the corners. - ClipMode::Clip => x != 1 && y != 1, - // Create render tasks for all segments (the - // center will be skipped). - ClipMode::ClipOut => true, - } - } - for y in 0 .. y_coords.len()-1 { let y0 = y_coords[y]; let y1 = y_coords[y+1]; @@ -340,16 +327,6 @@ pub fn push_quad( } for x in 0 .. x_coords.len()-1 { - // We'll create render tasks and segments for the corners in a - // separate loop. - if should_create_task(mode, x, y) { - continue; - } - - if mode == ClipMode::ClipOut && x == 1 && y == 1 { - continue; - } - let x0 = x_coords[x]; let x1 = x_coords[x+1]; @@ -357,73 +334,33 @@ pub fn push_quad( continue; } + let create_task = match mode { + ClipMode::Clip => { + // Only create render tasks for the corners. + x != 1 && y != 1 + } + ClipMode::ClipOut => { + if x == 1 && y == 1 { + continue; + } + + true + } + }; + let rect = DeviceIntRect::new(point2(x0, y0), point2(x1, y1)); - let device_rect = match rect.intersection(&clipped_surface_rect) { + let rect = match rect.intersection(&clipped_surface_rect) { Some(rect) => rect, None => { continue; } }; - scratch.quad_segments.push(QuadSegment { - rect: device_rect.to_f32().cast_unit(), - task_id: RenderTaskId::INVALID, - }); - } - } - - if !scratch.quad_segments.is_empty() { - add_pattern_prim( - pattern, - prim_instance_index, - unclipped_surface_rect.cast_unit(), - quad_flags, - frame_state, - targets, - &scratch.quad_segments, - ); - } - - scratch.quad_segments.clear(); - // Only create render tasks for the corners. - for y in 0 .. y_coords.len()-1 { - let y0 = y_coords[y]; - let y1 = y_coords[y+1]; - - if y1 <= y0 { - continue; - } - - for x in 0 .. x_coords.len()-1 { - if !should_create_task(mode, x, y) { - continue; - } - - if mode == ClipMode::ClipOut && x == 1 && y == 1 { - continue; - } - - let x0 = x_coords[x]; - let x1 = x_coords[x+1]; - - if x1 <= x0 { - continue; - } - - let rect = DeviceIntRect::new(point2(x0, y0), point2(x1, y1)); - - let device_rect = match rect.intersection(&clipped_surface_rect) { - Some(rect) => rect, - None => { - continue; - } - }; - - let task_id = add_render_task_with_mask( + let segment = add_segment( pattern, - device_rect.size(), - device_rect.min.to_f32(), + &rect, + create_task, clip_chain, prim_spatial_node_index, pic_context.raster_spatial_node_index, @@ -435,9 +372,7 @@ pub fn push_quad( false, frame_state, ); - - let rect = device_rect.to_f32().cast_unit(); - scratch.quad_segments.push(QuadSegment { rect, task_id }); + scratch.quad_segments.push(segment); } } @@ -527,10 +462,10 @@ fn get_prim_render_strategy( } } -fn add_render_task_with_mask( +fn add_segment( pattern: &Pattern, - task_size: DeviceIntSize, - content_origin: DevicePoint, + rect: &DeviceIntRect, + create_task: bool, clip_chain: &ClipChainInstance, prim_spatial_node_index: SpatialNodeIndex, raster_spatial_node_index: SpatialNodeIndex, @@ -541,73 +476,49 @@ fn add_render_task_with_mask( device_pixel_scale: DevicePixelScale, needs_scissor_rect: bool, frame_state: &mut FrameBuildingState, -) -> RenderTaskId { - let task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_prim( - pattern.kind, - pattern.shader_input, - prim_spatial_node_index, - raster_spatial_node_index, - device_pixel_scale, - content_origin, - prim_address_f, - transform_id, - aa_flags, - quad_flags, - clip_chain.clips_range, - needs_scissor_rect, - ), - )); +) -> QuadSegment { + let task_size = rect.size(); + let rect = rect.to_f32(); + let content_origin = rect.min; - let masks = MaskSubPass { - clip_node_range: clip_chain.clips_range, - prim_spatial_node_index, - prim_address_f, + let task_id = if create_task { + let task_id = frame_state.rg_builder.add().init(RenderTask::new_dynamic( + task_size, + RenderTaskKind::new_prim( + pattern.kind, + pattern.shader_input, + prim_spatial_node_index, + raster_spatial_node_index, + device_pixel_scale, + content_origin, + prim_address_f, + transform_id, + aa_flags, + quad_flags, + clip_chain.clips_range, + needs_scissor_rect, + ), + )); + + let masks = MaskSubPass { + clip_node_range: clip_chain.clips_range, + prim_spatial_node_index, + prim_address_f, + }; + + let task = frame_state.rg_builder.get_task_mut(task_id); + task.add_sub_pass(SubPass::Masks { masks }); + + frame_state + .surface_builder + .add_child_render_task(task_id, frame_state.rg_builder); + + task_id + } else { + RenderTaskId::INVALID }; - let task = frame_state.rg_builder.get_task_mut(task_id); - task.add_sub_pass(SubPass::Masks { masks }); - - frame_state - .surface_builder - .add_child_render_task(task_id, frame_state.rg_builder); - - task_id -} - -fn add_pattern_prim( - pattern: &Pattern, - prim_instance_index: PrimitiveInstanceIndex, - rect: LayoutRect, - quad_flags: QuadFlags, - frame_state: &mut FrameBuildingState, - targets: &[CommandBufferIndex], - segments: &[QuadSegment], -) { - let prim_address = write_prim_blocks( - &mut frame_state.frame_gpu_data.f32, - rect, - rect, - pattern.base_color, - segments, - ); - - frame_state.set_segments(segments, targets); - - frame_state.push_cmd( - &PrimitiveCommand::quad( - pattern.kind, - pattern.shader_input, - prim_instance_index, - prim_address, - TransformPaletteId::IDENTITY, - quad_flags, - // TODO(gw): No AA on composite, unless we use it to apply 2d clips - EdgeAaSegmentMask::empty(), - ), - targets, - ); + QuadSegment { rect: rect.cast_unit(), task_id } } fn add_composite_prim( diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs index c7c91f679a00..ede8e7897ccb 100644 --- a/gfx/wr/webrender/src/render_target.rs +++ b/gfx/wr/webrender/src/render_target.rs @@ -263,7 +263,7 @@ impl RenderTarget for ColorRenderTarget { used_rect, resolve_ops: Vec::new(), clear_color: Some(ColorF::TRANSPARENT), - prim_instances: [Vec::new(), Vec::new(), Vec::new(), Vec::new()], + prim_instances: [Vec::new(), Vec::new()], prim_instances_with_scissor: FastHashMap::default(), clip_masks: ClipMaskInstanceList::new(), } diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs index d7d81e072d94..a70d3eca1856 100644 --- a/gfx/wr/webrender/src/renderer/mod.rs +++ b/gfx/wr/webrender/src/renderer/mod.rs @@ -193,11 +193,11 @@ const GPU_TAG_CACHE_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag { label: "C_LinearGradient", color: debug_colors::BROWN, }; -const GPU_TAG_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { +const GPU_TAG_CACHE_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "C_RadialGradient", color: debug_colors::BROWN, }; -const GPU_TAG_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag { +const GPU_TAG_CACHE_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag { label: "C_ConicGradient", color: debug_colors::BROWN, }; @@ -288,8 +288,6 @@ impl BatchKind { } BatchKind::TextRun(_) => GPU_TAG_PRIM_TEXT_RUN, BatchKind::Quad(PatternKind::ColorOrTexture) => GPU_TAG_PRIMITIVE, - BatchKind::Quad(PatternKind::RadialGradient) => GPU_TAG_RADIAL_GRADIENT, - BatchKind::Quad(PatternKind::ConicGradient) => GPU_TAG_CONIC_GRADIENT, BatchKind::Quad(PatternKind::Mask) => GPU_TAG_INDIRECT_MASK, } } @@ -4071,7 +4069,7 @@ impl Renderer { // Draw any radial gradients for this target. if !target.radial_gradients.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_RADIAL_GRADIENT); + let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_RADIAL_GRADIENT); self.set_blend(false, FramebufferKind::Other); @@ -4097,7 +4095,7 @@ impl Renderer { // Draw any conic gradients for this target. if !target.conic_gradients.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CONIC_GRADIENT); + let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_CONIC_GRADIENT); self.set_blend(false, FramebufferKind::Other); diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs index 6b50c5a2b9e4..96e8982aa0bf 100644 --- a/gfx/wr/webrender/src/renderer/shade.rs +++ b/gfx/wr/webrender/src/renderer/shade.rs @@ -632,8 +632,6 @@ pub struct Shaders { ps_split_composite: LazilyCompiledShader, pub ps_quad_textured: LazilyCompiledShader, - pub ps_quad_radial_gradient: LazilyCompiledShader, - pub ps_quad_conic_gradient: LazilyCompiledShader, pub ps_mask: LazilyCompiledShader, pub ps_mask_fast: LazilyCompiledShader, pub ps_clear: LazilyCompiledShader, @@ -890,26 +888,6 @@ impl Shaders { profile, )?; - let ps_quad_radial_gradient = LazilyCompiledShader::new( - ShaderKind::Primitive, - "ps_quad_radial_gradient", - &[], - device, - options.precache_flags, - &shader_list, - profile, - )?; - - let ps_quad_conic_gradient = LazilyCompiledShader::new( - ShaderKind::Primitive, - "ps_quad_conic_gradient", - &[], - device, - options.precache_flags, - &shader_list, - profile, - )?; - let ps_split_composite = LazilyCompiledShader::new( ShaderKind::Primitive, "ps_split_composite", @@ -1144,8 +1122,6 @@ impl Shaders { ps_text_run, ps_text_run_dual_source, ps_quad_textured, - ps_quad_radial_gradient, - ps_quad_conic_gradient, ps_mask, ps_mask_fast, ps_split_composite, @@ -1184,8 +1160,6 @@ impl Shaders { ) -> &mut LazilyCompiledShader { match pattern { PatternKind::ColorOrTexture => &mut self.ps_quad_textured, - PatternKind::RadialGradient => &mut self.ps_quad_radial_gradient, - PatternKind::ConicGradient => &mut self.ps_quad_conic_gradient, PatternKind::Mask => unreachable!(), } } @@ -1201,12 +1175,6 @@ impl Shaders { BatchKind::Quad(PatternKind::ColorOrTexture) => { &mut self.ps_quad_textured } - BatchKind::Quad(PatternKind::RadialGradient) => { - &mut self.ps_quad_radial_gradient - } - BatchKind::Quad(PatternKind::ConicGradient) => { - &mut self.ps_quad_conic_gradient - } BatchKind::Quad(PatternKind::Mask) => { unreachable!(); } @@ -1337,8 +1305,6 @@ impl Shaders { self.cs_border_segment.deinit(device); self.ps_split_composite.deinit(device); self.ps_quad_textured.deinit(device); - self.ps_quad_radial_gradient.deinit(device); - self.ps_quad_conic_gradient.deinit(device); self.ps_mask.deinit(device); self.ps_mask_fast.deinit(device); self.ps_clear.deinit(device); diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs index 0340e15a9fb7..780a24a1aa65 100644 --- a/gfx/wr/webrender_build/src/shader_features.rs +++ b/gfx/wr/webrender_build/src/shader_features.rs @@ -228,10 +228,6 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { shaders.insert("ps_quad_textured", vec![base_prim_features.finish()]); - shaders.insert("ps_quad_radial_gradient", vec![base_prim_features.finish()]); - - shaders.insert("ps_quad_conic_gradient", vec![base_prim_features.finish()]); - shaders.insert("ps_clear", vec![base_prim_features.finish()]); shaders.insert("ps_copy", vec![base_prim_features.finish()]); diff --git a/gfx/wr/wrench/reftests/gradient/radial-border-radius-large-ref.png b/gfx/wr/wrench/reftests/gradient/radial-border-radius-large-ref.png deleted file mode 100644 index b86542ad0bc2510611d7b5b9cb2316a12cdaea12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17159 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