зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
aeeef327a0
Коммит
bdeac260c5
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче