From a6d2963a8fe5c927d77803382518fb51de88a9a1 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Thu, 18 Jul 2024 04:21:31 +0000 Subject: [PATCH] Bug 1907481 - Add support for patterns that have texture input sources r=gfx-reviewers,lsalzman Not currently used in this patch, but will be by the upcoming box-shadow patches. Differential Revision: https://phabricator.services.mozilla.com/D216365 --- webrender/res/gpu_buffer.glsl | 11 +++++ webrender/res/ps_quad.glsl | 12 +++-- webrender/src/pattern.rs | 20 +++++++- webrender/src/prepare.rs | 7 ++- webrender/src/prim_store/gradient/conic.rs | 3 +- webrender/src/prim_store/gradient/radial.rs | 3 +- webrender/src/quad.rs | 51 +++++++++++++++------ webrender/src/render_target.rs | 31 ++++++++----- webrender/src/render_task.rs | 3 ++ webrender/src/renderer/gpu_buffer.rs | 17 +++++-- webrender/src/renderer/mod.rs | 49 ++++++++++---------- 11 files changed, 142 insertions(+), 65 deletions(-) diff --git a/webrender/res/gpu_buffer.glsl b/webrender/res/gpu_buffer.glsl index 4923a28ef9..efd9a38637 100644 --- a/webrender/res/gpu_buffer.glsl +++ b/webrender/res/gpu_buffer.glsl @@ -42,6 +42,17 @@ vec4[4] fetch_from_gpu_buffer_4f(HIGHP_FS_ADDRESS int address) { ); } +vec4[5] fetch_from_gpu_buffer_5f(HIGHP_FS_ADDRESS int address) { + ivec2 uv = get_gpu_buffer_uv(address); + return vec4[5]( + TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(0, 0)), + TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(1, 0)), + TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(2, 0)), + TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(3, 0)), + TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(4, 0)) + ); +} + ivec4 fetch_from_gpu_buffer_1i(HIGHP_FS_ADDRESS int address) { ivec2 uv = get_gpu_buffer_uv(address); return texelFetch(sGpuBufferI, uv, 0); diff --git a/webrender/res/ps_quad.glsl b/webrender/res/ps_quad.glsl index 835109781f..a456c99c6b 100644 --- a/webrender/res/ps_quad.glsl +++ b/webrender/res/ps_quad.glsl @@ -96,6 +96,7 @@ struct PrimitiveInfo { struct QuadPrimitive { RectWithEndpoint bounds; RectWithEndpoint clip; + RectWithEndpoint uv_rect; vec4 pattern_scale_offset; vec4 color; }; @@ -103,7 +104,7 @@ struct QuadPrimitive { QuadSegment fetch_segment(int base, int index) { QuadSegment seg; - vec4 texels[2] = fetch_from_gpu_buffer_2f(base + 4 + index * 2); + vec4 texels[2] = fetch_from_gpu_buffer_2f(base + 5 + index * 2); seg.rect = RectWithEndpoint(texels[0].xy, texels[0].zw); seg.uv_rect = RectWithEndpoint(texels[1].xy, texels[1].zw); @@ -114,12 +115,13 @@ QuadSegment fetch_segment(int base, int index) { QuadPrimitive fetch_primitive(int index) { QuadPrimitive prim; - vec4 texels[4] = fetch_from_gpu_buffer_4f(index); + vec4 texels[5] = fetch_from_gpu_buffer_5f(index); prim.bounds = RectWithEndpoint(texels[0].xy, texels[0].zw); prim.clip = RectWithEndpoint(texels[1].xy, texels[1].zw); - prim.pattern_scale_offset = texels[2]; - prim.color = texels[3]; + prim.uv_rect = RectWithEndpoint(texels[2].xy, texels[2].zw); + prim.pattern_scale_offset = texels[3]; + prim.color = texels[4]; return prim; } @@ -246,7 +248,7 @@ PrimitiveInfo quad_primive_info(void) { QuadSegment seg; if (qi.segment_index == INVALID_SEGMENT_INDEX) { seg.rect = prim.bounds; - seg.uv_rect = RectWithEndpoint(vec2(0.0), vec2(0.0)); + seg.uv_rect = prim.uv_rect; } else { seg = fetch_segment(qi.prim_address_f, qi.segment_index); } diff --git a/webrender/src/pattern.rs b/webrender/src/pattern.rs index d7e5740829..795f44f37b 100644 --- a/webrender/src/pattern.rs +++ b/webrender/src/pattern.rs @@ -4,7 +4,7 @@ use api::ColorF; -use crate::{renderer::GpuBufferBuilder, scene::SceneProperties}; +use crate::{render_task_graph::RenderTaskId, renderer::GpuBufferBuilder, scene::SceneProperties}; #[repr(u32)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -42,6 +42,21 @@ impl Default for PatternShaderInput { } } +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct PatternTextureInput { + pub task_id: RenderTaskId, +} + +impl Default for PatternTextureInput { + fn default() -> Self { + PatternTextureInput { + task_id: RenderTaskId::INVALID, + } + } +} + pub struct PatternBuilderContext<'a> { pub scene_properties: &'a SceneProperties, } @@ -62,6 +77,7 @@ pub trait PatternBuilder { pub struct Pattern { pub kind: PatternKind, pub shader_input: PatternShaderInput, + pub texture_input: PatternTextureInput, pub base_color: ColorF, pub is_opaque: bool, } @@ -71,6 +87,7 @@ impl Pattern { Pattern { kind: PatternKind::ColorOrTexture, shader_input: PatternShaderInput::default(), + texture_input: PatternTextureInput::default(), base_color: color, is_opaque: color.a >= 1.0, } @@ -81,6 +98,7 @@ impl Pattern { Pattern { kind: PatternKind::ColorOrTexture, shader_input: PatternShaderInput::default(), + texture_input: PatternTextureInput::default(), base_color: ColorF::BLACK, is_opaque: false, } diff --git a/webrender/src/prepare.rs b/webrender/src/prepare.rs index e495c5a03e..838b1f8548 100644 --- a/webrender/src/prepare.rs +++ b/webrender/src/prepare.rs @@ -6,7 +6,7 @@ //! //! TODO: document this! -use api::{PremultipliedColorF, PropertyBinding}; +use api::{ColorF, PropertyBinding}; use api::{BoxShadowClipMode, BorderStyle, ClipMode}; use api::units::*; use euclid::Scale; @@ -16,6 +16,7 @@ use crate::command_buffer::{PrimitiveCommand, CommandBufferIndex}; use crate::image_tiling::{self, Repetition}; use crate::border::{get_max_scale_for_border, build_border_instances}; use crate::clip::{ClipStore, ClipNodeRange}; +use crate::pattern::Pattern; use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainInstance, ClipItemKind}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; @@ -952,11 +953,13 @@ fn prepare_interned_prim_for_render( .clipped_local_rect .cast_unit(); + let pattern = Pattern::color(ColorF::WHITE); + let prim_address_f = quad::write_prim_blocks( &mut frame_state.frame_gpu_data.f32, prim_local_rect, prim_instance.vis.clip_chain.local_clip_rect, - PremultipliedColorF::WHITE, + &pattern, &[], ScaleOffset::identity(), ); diff --git a/webrender/src/prim_store/gradient/conic.rs b/webrender/src/prim_store/gradient/conic.rs index 9cc7303330..e472396eec 100644 --- a/webrender/src/prim_store/gradient/conic.rs +++ b/webrender/src/prim_store/gradient/conic.rs @@ -11,7 +11,7 @@ use euclid::vec2; use api::{ColorF, ExtendMode, GradientStop, PremultipliedColorF}; use api::units::*; -use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput}; +use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput, PatternTextureInput}; use crate::scene_building::IsVisible; use crate::frame_builder::FrameBuildingState; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; @@ -454,6 +454,7 @@ pub fn conic_gradient_pattern( gradient_address.as_int(), stops_address.as_int(), ), + texture_input: PatternTextureInput::default(), base_color: ColorF::WHITE, is_opaque, } diff --git a/webrender/src/prim_store/gradient/radial.rs b/webrender/src/prim_store/gradient/radial.rs index 562b6e3c62..6ab1e298c3 100644 --- a/webrender/src/prim_store/gradient/radial.rs +++ b/webrender/src/prim_store/gradient/radial.rs @@ -11,7 +11,7 @@ use euclid::{vec2, size2}; use api::{ColorF, ColorU, ExtendMode, GradientStop, PremultipliedColorF}; use api::units::*; -use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput}; +use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput, PatternTextureInput}; use crate::scene_building::IsVisible; use crate::frame_builder::FrameBuildingState; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; @@ -587,6 +587,7 @@ pub fn radial_gradient_pattern( gradient_address.as_int(), stops_address.as_int(), ), + texture_input: PatternTextureInput::default(), base_color: ColorF::WHITE, is_opaque, } diff --git a/webrender/src/quad.rs b/webrender/src/quad.rs index 822681be5b..a025542350 100644 --- a/webrender/src/quad.rs +++ b/webrender/src/quad.rs @@ -2,7 +2,7 @@ * 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/. */ -use api::{units::*, ClipMode, PremultipliedColorF}; +use api::{units::*, ClipMode}; use euclid::point2; use crate::batch::{BatchKey, BatchKind, BatchTextures}; @@ -143,7 +143,7 @@ pub fn prepare_quad( &mut frame_state.frame_gpu_data.f32, *local_rect, clip_chain.local_clip_rect, - pattern.base_color.premultiplied(), + &pattern, &[], ScaleOffset::identity(), ); @@ -164,6 +164,15 @@ pub fn prepare_quad( prim_spatial_node_index, targets, ); + + // If the pattern samples from a texture, add it as a dependency + // of the surface we're drawing directly on to. + if pattern.texture_input.task_id != RenderTaskId::INVALID { + frame_state + .surface_builder + .add_child_render_task(pattern.texture_input.task_id, frame_state.rg_builder); + } + return; } @@ -171,6 +180,17 @@ pub fn prepare_quad( let Some(clipped_surface_rect) = surface.get_surface_rect( &clip_chain.pic_coverage_rect, frame_context.spatial_tree ) else { + // In the rare case of getting to here with a prim that wasn't culled + // earlier, but that gets clipped here (e.g. float issues), we need + // to add any render task created by the pattern as a dependency to + // the surface so it gets freed when building the graph. + // TODO(gw): We should maybe have a proper way to cancel render tasks... + if pattern.texture_input.task_id != RenderTaskId::INVALID { + frame_state + .surface_builder + .add_child_render_task(pattern.texture_input.task_id, frame_state.rg_builder); + } + return; }; @@ -707,9 +727,16 @@ fn add_render_task_with_mask( aa_flags, quad_flags, needs_scissor_rect, + pattern.texture_input.task_id, ), )); + // If the pattern samples from a texture, add it as a dependency + // of the indirect render task that relies on it. + if pattern.texture_input.task_id != RenderTaskId::INVALID { + frame_state.rg_builder.add_dependency(task_id, pattern.texture_input.task_id); + } + if clips_range.count > 0 { let masks = MaskSubPass { clip_node_range: clips_range, @@ -743,7 +770,7 @@ fn add_pattern_prim( &mut frame_state.frame_gpu_data.f32, rect, clip_rect, - pattern.base_color.premultiplied(), + pattern, segments, pattern_transform, ); @@ -790,7 +817,7 @@ fn add_composite_prim( // in the quad primitive). However, passing opaque white // here causes glitches with Adreno GPUs on Windows specifically // (See bug 1897444). - pattern.base_color.premultiplied(), + pattern, segments, ScaleOffset::identity(), ); @@ -823,27 +850,21 @@ pub fn write_prim_blocks( builder: &mut GpuBufferBuilderF, prim_rect: LayoutRect, clip_rect: LayoutRect, - color: PremultipliedColorF, + pattern: &Pattern, segments: &[QuadSegment], scale_offset: ScaleOffset, ) -> GpuBufferAddress { - let mut writer = builder.write_blocks(4 + segments.len() * 2); + let mut writer = builder.write_blocks(5 + segments.len() * 2); writer.push_one(prim_rect); writer.push_one(clip_rect); + writer.push_render_task(pattern.texture_input.task_id); writer.push_one(scale_offset); - writer.push_one(color); + writer.push_one(pattern.base_color.premultiplied()); for segment in segments { writer.push_one(segment.rect); - match segment.task_id { - RenderTaskId::INVALID => { - writer.push_one([0.0; 4]); - } - task_id => { - writer.push_render_task(task_id); - } - } + writer.push_render_task(segment.task_id) } writer.finish() diff --git a/webrender/src/render_target.rs b/webrender/src/render_target.rs index 88cbbbb29c..e1edf2d232 100644 --- a/webrender/src/render_target.rs +++ b/webrender/src/render_target.rs @@ -3,16 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{units::*, PremultipliedColorF}; +use api::units::*; use api::{ColorF, LineOrientation, BorderStyle}; use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures}; use crate::batch::{ClipBatcher, BatchBuilder, INVALID_SEGMENT_INDEX, ClipMaskInstanceList}; use crate::command_buffer::{CommandBufferList, QuadFlags}; -use crate::pattern::{PatternKind, PatternShaderInput}; +use crate::pattern::{Pattern, PatternKind, PatternShaderInput}; use crate::segment::EdgeAaSegmentMask; use crate::spatial_tree::SpatialTree; use crate::clip::{ClipStore, ClipItemKind}; -use crate::frame_builder::{FrameGlobalResources}; +use crate::frame_builder::FrameGlobalResources; use crate::gpu_cache::{GpuCache, GpuCacheAddress}; use crate::gpu_types::{BorderInstance, SvgFilterInstance, SVGFEFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; use crate::gpu_types::{TransformPalette, ZBufferIdGenerator, MaskInstance, ClipSpace}; @@ -31,7 +31,7 @@ use crate::render_task::{RenderTaskKind, RenderTaskAddress, SubPass}; use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo, MaskSubPass, SVGFEFilterTask}; use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; use crate::resource_cache::ResourceCache; -use crate::spatial_tree::{SpatialNodeIndex}; +use crate::spatial_tree::SpatialNodeIndex; use crate::util::ScaleOffset; @@ -229,8 +229,8 @@ pub struct ColorRenderTarget { pub resolve_ops: Vec, pub clear_color: Option, - pub prim_instances: [Vec; NUM_PATTERNS], - pub prim_instances_with_scissor: FastHashMap<(DeviceIntRect, PatternKind), Vec>, + pub prim_instances: [FastHashMap>; NUM_PATTERNS], + pub prim_instances_with_scissor: FastHashMap<(DeviceIntRect, PatternKind), FastHashMap>>, pub clip_masks: ClipMaskInstanceList, } @@ -256,7 +256,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: [FastHashMap::default(), FastHashMap::default(), FastHashMap::default(), FastHashMap::default()], prim_instances_with_scissor: FastHashMap::default(), clip_masks: ClipMaskInstanceList::new(), } @@ -380,18 +380,23 @@ impl RenderTarget for ColorRenderTarget { info.quad_flags, info.edge_flags, INVALID_SEGMENT_INDEX as u8, - RenderTaskId::INVALID, + info.texture_input, ZBufferId(0), render_tasks, gpu_buffer_builder, - |_, instance| { + |key, instance| { if info.prim_needs_scissor_rect { self.prim_instances_with_scissor .entry((target_rect, info.pattern)) + .or_insert(FastHashMap::default()) + .entry(key.textures.input.colors[0]) .or_insert(Vec::new()) .push(instance); } else { - self.prim_instances[info.pattern as usize].push(instance); + self.prim_instances[info.pattern as usize] + .entry(key.textures.input.colors[0]) + .or_insert(Vec::new()) + .push(instance); } } ); @@ -1235,6 +1240,7 @@ fn build_mask_tasks( raster_spatial_node_index, ); + let pattern = Pattern::color(ColorF::WHITE); let clip_needs_scissor_rect = !is_same_coord_system; let mut quad_flags = QuadFlags::IS_MASK; @@ -1247,7 +1253,7 @@ fn build_mask_tasks( &mut gpu_buffer_builder.f32, rect, rect, - PremultipliedColorF::WHITE, + &pattern, &[QuadSegment { rect: tile.tile_rect, task_id: tile.task_id, @@ -1304,6 +1310,7 @@ fn build_mask_tasks( let (clip_space, clip_transform_id, main_prim_address, prim_transform_id, is_same_coord_system) = if raster_clip { let prim_transform_id = TransformPaletteId::IDENTITY; + let pattern = Pattern::color(ColorF::WHITE); let clip_transform_id = transforms.get_id( raster_spatial_node_index, @@ -1315,7 +1322,7 @@ fn build_mask_tasks( &mut gpu_buffer_builder.f32, task_world_rect.cast_unit(), task_world_rect.cast_unit(), - PremultipliedColorF::WHITE, + &pattern, &[], ScaleOffset::identity(), ); diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 33ed7f95c7..8c7cc6b72c 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -197,6 +197,7 @@ pub struct PrimTask { pub edge_flags: EdgeAaSegmentMask, pub quad_flags: QuadFlags, pub prim_needs_scissor_rect: bool, + pub texture_input: RenderTaskId, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -546,6 +547,7 @@ impl RenderTaskKind { edge_flags: EdgeAaSegmentMask, quad_flags: QuadFlags, prim_needs_scissor_rect: bool, + texture_input: RenderTaskId, ) -> Self { RenderTaskKind::Prim(PrimTask { pattern, @@ -558,6 +560,7 @@ impl RenderTaskKind { edge_flags, quad_flags, prim_needs_scissor_rect, + texture_input, }) } diff --git a/webrender/src/renderer/gpu_buffer.rs b/webrender/src/renderer/gpu_buffer.rs index 8ecf912239..659c386972 100644 --- a/webrender/src/renderer/gpu_buffer.rs +++ b/webrender/src/renderer/gpu_buffer.rs @@ -238,11 +238,18 @@ impl<'a, T> GpuBufferWriter<'a, T> where T: Texel { /// Push a reference to a render task in to the writer. Once the render /// task graph is resolved, this will be patched with the UV rect of the task pub fn push_render_task(&mut self, task_id: RenderTaskId) { - self.deferred.push(DeferredBlock { - task_id, - index: self.buffer.len(), - }); - self.buffer.push(T::default()); + match task_id { + RenderTaskId::INVALID => { + self.buffer.push(T::default()); + } + task_id => { + self.deferred.push(DeferredBlock { + task_id, + index: self.buffer.len(), + }); + self.buffer.push(T::default()); + } + } } /// Close this writer, returning the GPU address of this set of block(s). diff --git a/webrender/src/renderer/mod.rs b/webrender/src/renderer/mod.rs index 0bbbb51535..1fedbb67d9 100644 --- a/webrender/src/renderer/mod.rs +++ b/webrender/src/renderer/mod.rs @@ -2182,8 +2182,8 @@ impl Renderer { fn handle_prims( &mut self, draw_target: &DrawTarget, - prim_instances: &[Vec], - prim_instances_with_scissor: &FastHashMap<(DeviceIntRect, PatternKind), Vec>, + prim_instances: &[FastHashMap>], + prim_instances_with_scissor: &FastHashMap<(DeviceIntRect, PatternKind), FastHashMap>>, projection: &default::Transform3D, stats: &mut RendererStats, ) { @@ -2192,12 +2192,10 @@ impl Renderer { { let _timer = self.gpu_profiler.start_timer(GPU_TAG_INDIRECT_PRIM); - if prim_instances.iter().any(|instances| !instances.is_empty()) { - self.set_blend(false, FramebufferKind::Other); - } + self.set_blend(false, FramebufferKind::Other); - for (pattern_idx, prim_instances) in prim_instances.iter().enumerate() { - if prim_instances.is_empty() { + for (pattern_idx, prim_instances_map) in prim_instances.iter().enumerate() { + if prim_instances_map.is_empty() { continue; } let pattern = PatternKind::from_u32(pattern_idx as u32); @@ -2210,15 +2208,16 @@ impl Renderer { &mut self.profile, ); - // TODO: Some patterns will need to be able to sample textures. - let texture_bindings = BatchTextures::empty(); + for (texture_source, prim_instances) in prim_instances_map { + let texture_bindings = BatchTextures::composite_rgb(*texture_source); - self.draw_instanced_batch( - prim_instances, - VertexArrayKind::Primitive, - &texture_bindings, - stats, - ); + self.draw_instanced_batch( + prim_instances, + VertexArrayKind::Primitive, + &texture_bindings, + stats, + ); + } } if !prim_instances_with_scissor.is_empty() { @@ -2228,7 +2227,7 @@ impl Renderer { let mut prev_pattern = None; - for ((scissor_rect, pattern), prim_instances) in prim_instances_with_scissor { + for ((scissor_rect, pattern), prim_instances_map) in prim_instances_with_scissor { if prev_pattern != Some(*pattern) { prev_pattern = Some(*pattern); self.shaders.borrow_mut().get_quad_shader(*pattern).bind( @@ -2241,13 +2240,17 @@ impl Renderer { } self.device.set_scissor_rect(draw_target.to_framebuffer_rect(*scissor_rect)); - // TODO: hook up the right pattern. - self.draw_instanced_batch( - prim_instances, - VertexArrayKind::Primitive, - &BatchTextures::empty(), - stats, - ); + + for (texture_source, prim_instances) in prim_instances_map { + let texture_bindings = BatchTextures::composite_rgb(*texture_source); + + self.draw_instanced_batch( + prim_instances, + VertexArrayKind::Primitive, + &texture_bindings, + stats, + ); + } } self.device.disable_scissor();