Bug 1885571 - Quad radial gradient shader. r=gw

For now we skip the quad path if the gradient is tiled. The plan is to add a repeating quad pattern that can repeat the result of a render task and get all quad patterns to use that.

Differential Revision: https://phabricator.services.mozilla.com/D204774
This commit is contained in:
Nicolas Silva 2024-04-16 16:26:27 +00:00
Родитель aeeef327a0
Коммит bdeac260c5
11 изменённых файлов: 225 добавлений и 22 удалений

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

@ -0,0 +1,81 @@
/* 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

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

@ -10,12 +10,13 @@ use api::{ColorF, PremultipliedColorF};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PatternKind {
ColorOrTexture = 0,
RadialGradient = 1,
Mask = 1,
Mask = 2,
// When adding patterns, don't forget to update the NUM_PATTERNS constant.
}
pub const NUM_PATTERNS: u32 = 2;
pub const NUM_PATTERNS: u32 = 3;
impl PatternKind {
pub fn from_u32(val: u32) -> Self {

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

@ -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::GradientGpuBlockBuilder;
use crate::prim_store::gradient::{radial_gradient_pattern, GradientGpuBlockBuilder};
use crate::render_backend::DataStores;
use crate::render_task_graph::RenderTaskId;
use crate::render_task_cache::RenderTaskCacheKeyKind;
@ -211,6 +211,32 @@ 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
@ -218,18 +244,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 { ref mut use_legacy_path, .. } => {
*use_legacy_path = !can_use_clip_chain_for_quad_path(
PrimitiveInstanceKind::Rectangle { use_legacy_path: ref mut no_quads, .. }
| PrimitiveInstanceKind::RadialGradient { cached: ref mut no_quads, .. }
=> {
*no_quads = disable_quad_path || !can_use_clip_chain_for_quad_path(
&prim_instance.vis.clip_chain,
frame_state.clip_store,
data_stores,
);
*use_legacy_path
}
PrimitiveInstanceKind::Picture { .. } => {
false
*no_quads
}
PrimitiveInstanceKind::Picture { .. } => false,
_ => true,
};
@ -780,12 +806,45 @@ fn prepare_interned_prim_for_render(
}
}
}
PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, .. } => {
PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, cached, .. } => {
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.
@ -810,9 +869,6 @@ 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, .. } => {
profile_scope!("ConicGradient");

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

@ -11,6 +11,7 @@
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};
@ -22,8 +23,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;
use crate::picture::{SurfaceIndex};
use crate::renderer::{GpuBufferAddress, GpuBufferBuilder};
use crate::picture::SurfaceIndex;
use std::{hash, ops::{Deref, DerefMut}};
use super::{
@ -295,6 +296,7 @@ impl InternablePrimitive for RadialGradient {
PrimitiveInstanceKind::RadialGradient {
data_handle,
visible_tiles_range: GradientTileRange::empty(),
cached: true,
}
}
}
@ -529,3 +531,45 @@ 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,
}
}

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

@ -1029,6 +1029,7 @@ 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.

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

@ -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()],
prim_instances: [Vec::new(), Vec::new(), Vec::new()],
prim_instances_with_scissor: FastHashMap::default(),
clip_masks: ClipMaskInstanceList::new(),
}

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

@ -193,7 +193,7 @@ const GPU_TAG_CACHE_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "C_LinearGradient",
color: debug_colors::BROWN,
};
const GPU_TAG_CACHE_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
const GPU_TAG_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "C_RadialGradient",
color: debug_colors::BROWN,
};
@ -288,6 +288,7 @@ 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::Mask) => GPU_TAG_INDIRECT_MASK,
}
}
@ -4069,7 +4070,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_CACHE_RADIAL_GRADIENT);
let _timer = self.gpu_profiler.start_timer(GPU_TAG_RADIAL_GRADIENT);
self.set_blend(false, FramebufferKind::Other);

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

@ -632,6 +632,7 @@ pub struct Shaders {
ps_split_composite: LazilyCompiledShader,
pub ps_quad_textured: LazilyCompiledShader,
pub ps_quad_radial_gradient: LazilyCompiledShader,
pub ps_mask: LazilyCompiledShader,
pub ps_mask_fast: LazilyCompiledShader,
pub ps_clear: LazilyCompiledShader,
@ -888,6 +889,16 @@ 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_split_composite = LazilyCompiledShader::new(
ShaderKind::Primitive,
"ps_split_composite",
@ -1122,6 +1133,7 @@ impl Shaders {
ps_text_run,
ps_text_run_dual_source,
ps_quad_textured,
ps_quad_radial_gradient,
ps_mask,
ps_mask_fast,
ps_split_composite,
@ -1160,6 +1172,7 @@ impl Shaders {
) -> &mut LazilyCompiledShader {
match pattern {
PatternKind::ColorOrTexture => &mut self.ps_quad_textured,
PatternKind::RadialGradient => &mut self.ps_quad_radial_gradient,
PatternKind::Mask => unreachable!(),
}
}
@ -1175,6 +1188,9 @@ impl Shaders {
BatchKind::Quad(PatternKind::ColorOrTexture) => {
&mut self.ps_quad_textured
}
BatchKind::Quad(PatternKind::RadialGradient) => {
&mut self.ps_quad_radial_gradient
}
BatchKind::Quad(PatternKind::Mask) => {
unreachable!();
}
@ -1305,6 +1321,7 @@ 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_mask.deinit(device);
self.ps_mask_fast.deinit(device);
self.ps_clear.deinit(device);

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

@ -228,6 +228,8 @@ 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_clear", vec![base_prim_features.finish()]);
shaders.insert("ps_copy", vec![base_prim_features.finish()]);

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

@ -119,7 +119,7 @@ fuzzy-range(<=1,1) == gradient_cache_hardstop_clip.yaml gradient_cache_hardstop_
fuzzy(2,23000) == linear-large.yaml linear-large-ref.yaml
== conic-large.yaml conic-large-ref.yaml
fuzzy-if(env(android,device),254,1) == conic-large-hard-stop.yaml conic-large-hard-stop-ref.yaml # Android device is Samsung Galaxy A51
fuzzy(1,7000) == radial-large.yaml radial-large-ref.png
fuzzy(1,80000) == radial-large.yaml radial-large-ref.png
# crash tests
== linear-far-endpoints.yaml linear-far-endpoints.yaml

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

@ -47,8 +47,8 @@ fuzzy-if(Android,0-8,0-771) == radial-shape-farthest-corner-1a.html radial-shape
fails-if(gtkWidget) fuzzy(0-2,0-500) == radial-shape-farthest-corner-1b.html radial-shape-farthest-corner-1-ref.html
fuzzy(0-2,0-15000) fuzzy-if(Android,0-17,0-13320) == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
fuzzy(0-2,0-15000) fuzzy-if(Android,0-17,0-13320) == radial-shape-farthest-side-1b.html radial-shape-farthest-side-1-ref.html
== radial-size-1a.html radial-size-1-ref.html
== radial-size-1b.html radial-size-1-ref.html
fuzzy(0-1,0-100) == radial-size-1a.html radial-size-1-ref.html
fuzzy(0-1,0-100) == radial-size-1b.html radial-size-1-ref.html
fuzzy-if(Android,0-4,0-248) == radial-zero-length-1a.html radial-zero-length-1-ref.html
fuzzy-if(Android,0-4,0-248) == radial-zero-length-1b.html radial-zero-length-1-ref.html
fuzzy-if(Android,0-4,0-248) == radial-zero-length-1c.html radial-zero-length-1-ref.html