Bug 1958109 - Support rounded clip rects during (draw / non-native) compositing r=gfx-reviewers,nical

Support for this with native compositing will be added by follow up patches.

Differential Revision: https://phabricator.services.mozilla.com/D244264
This commit is contained in:
Glenn Watson
2025-04-10 03:21:16 +00:00
parent adea196294
commit 4d98eac59a
29 changed files with 761 additions and 79 deletions

View File

@@ -15,6 +15,13 @@
#include yuv
#endif
#ifndef WR_FEATURE_FAST_PATH
// Parameters for compositor clip
varying highp vec2 vNormalizedWorldPos;
flat varying highp vec2 vRoundedClipParams;
flat varying highp vec4 vRoundedClipRadii;
#endif
#ifdef WR_FEATURE_YUV
flat varying YUV_PRECISION vec3 vYcbcrBias;
flat varying YUV_PRECISION mat3 vRgbFromDebiasedYcbcr;
@@ -51,6 +58,11 @@ PER_INSTANCE attribute vec4 aColor;
PER_INSTANCE attribute vec4 aParams;
PER_INSTANCE attribute vec2 aFlip;
#ifndef WR_FEATURE_FAST_PATH
PER_INSTANCE attribute vec4 aDeviceRoundedClipRect;
PER_INSTANCE attribute vec4 aDeviceRoundedClipRadii;
#endif
#ifdef WR_FEATURE_YUV
// YUV treats these as a UV clip rect (clamp)
PER_INSTANCE attribute vec4 aUvRect0;
@@ -80,6 +92,13 @@ void main(void) {
// Clip the position to the world space clip rect
vec2 clipped_world_pos = clamp(world_pos, aDeviceClipRect.xy, aDeviceClipRect.zw);
#ifndef WR_FEATURE_FAST_PATH
vec2 half_clip_box_size = 0.5 * (aDeviceRoundedClipRect.zw - aDeviceRoundedClipRect.xy);
vNormalizedWorldPos = aDeviceRoundedClipRect.xy + half_clip_box_size - clipped_world_pos;
vRoundedClipParams = half_clip_box_size;
vRoundedClipRadii = aDeviceRoundedClipRadii;
#endif
// Derive the normalized UV from the clipped vertex position
vec2 uv = (clipped_world_pos - device_rect.xy) / (device_rect.zw - device_rect.xy);
@@ -160,6 +179,20 @@ void main(void) {
#endif
#ifdef WR_FRAGMENT_SHADER
#ifndef WR_FEATURE_FAST_PATH
// See https://www.shadertoy.com/view/4llXD7
// Notes:
// * pos is centered in the origin (so 0,0 is the center of the box).
// * The border radii must not be larger than half_box_size.
float sd_round_box(in vec2 pos, in vec2 half_box_size, in vec4 radii) {
radii.xy = (pos.x > 0.0) ? radii.xy : radii.zw;
radii.x = (pos.y > 0.0) ? radii.x : radii.y;
vec2 q = abs(pos) - half_box_size + radii.x;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radii.x;
}
#endif
void main(void) {
#ifdef WR_FEATURE_YUV
vec4 color = sample_yuv(
@@ -188,6 +221,27 @@ void main(void) {
vec4 color = vColor * texel;
#endif
#endif
// TODO(gw): Do we need to support this on ESSL1?
#ifndef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1
#ifndef WR_FEATURE_FAST_PATH
// Apply compositor clip
float aa_range = compute_aa_range(vNormalizedWorldPos);
float dist = sd_round_box(
vNormalizedWorldPos,
vRoundedClipParams,
vRoundedClipRadii
);
// Compute AA for the given dist and range.
float clip_alpha = distance_aa(aa_range, dist);
// Apply clip alpha
color *= clip_alpha;
#endif
#endif
write_output(color);
}
@@ -225,6 +279,27 @@ void swgl_drawSpanRGBA8() {
vec4 color = vColor;
vec4 uvBounds = vUVBounds;
#endif
// TODO(gw): Do we need to support this on ESSL1?
#ifndef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1
#ifndef WR_FEATURE_FAST_PATH
// Apply compositor clip
float aa_range = compute_aa_range(vNormalizedWorldPos);
float dist = sd_round_box(
vNormalizedWorldPos,
vRoundedClipParams,
vRoundedClipRadii
);
// Compute AA for the given dist and range.
float clip_alpha = distance_aa(aa_range, dist);
// Apply clip alpha
color *= clip_alpha;
#endif
#endif
if (color != vec4(1.0)) {
swgl_commitTextureColorRGBA8(sColor0, vUv, uvBounds, color);
} else {

View File

@@ -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::{ColorF, ExternalImageId, ImageBufferKind, ImageKey, ImageRendering, YuvFormat, YuvRangedColorSpace};
use api::{BorderRadius, ColorF, ExternalImageId, ImageBufferKind, ImageKey, ImageRendering, YuvFormat, YuvRangedColorSpace};
use api::units::*;
use api::ColorDepth;
use crate::image_source::resolve_image;
@@ -13,11 +13,12 @@ use crate::internal_types::{FrameAllocator, FrameMemory, FrameVec, TextureSource
use crate::picture::{ImageDependency, ResolvedSurfaceTexture, TileCacheInstance, TileId, TileSurface};
use crate::prim_store::DeferredResolve;
use crate::resource_cache::{ImageRequest, ResourceCache};
use crate::util::{Preallocator, ScaleOffset};
use crate::util::{extract_inner_rect_safe, Preallocator, ScaleOffset};
use crate::tile_cache::PictureCacheDebugInfo;
use crate::device::Device;
use crate::space::SpaceMapper;
use std::{ops, u64, os::raw::c_void};
use std::num::NonZeroUsize;
/*
Types and definitions related to compositing picture cache tiles
@@ -115,6 +116,8 @@ bitflags! {
const NO_UV_CLAMP = 1 << 0;
// The texture sample should not be modulated by a specified color.
const NO_COLOR_MODULATION = 1 << 1;
// Can skip applying clip mask.
const NO_CLIP_MASK = 1 << 2;
}
}
@@ -137,6 +140,12 @@ impl CompositorTransformIndex {
pub const INVALID: CompositorTransformIndex = CompositorTransformIndex(!0);
}
// Index in to the compositor clips stored in `CompositeState`
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone)]
pub struct CompositorClipIndex(NonZeroUsize);
/// Describes the geometry and surface of a tile to be composited
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -150,6 +159,7 @@ pub struct CompositeTile {
pub z_id: ZBufferId,
pub kind: TileKind,
pub transform_index: CompositorTransformIndex,
pub clip_index: Option<CompositorClipIndex>,
}
pub fn tile_kind(surface: &CompositeTileSurface, is_opaque: bool) -> TileKind {
@@ -595,6 +605,13 @@ pub struct CompositorTransform {
local_to_device: ScaleOffset,
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CompositorClip {
pub rect: DeviceRect,
pub radius: BorderRadius,
}
/// The list of tiles to be drawn this frame
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -629,6 +646,8 @@ pub struct CompositeState {
pub transforms: FrameVec<CompositorTransform>,
/// Whether we have low quality pinch zoom enabled
low_quality_pinch_zoom: bool,
/// List of registered clips used by picture cache and/or external surfaces
pub clips: FrameVec<CompositorClip>,
}
impl CompositeState {
@@ -641,6 +660,14 @@ impl CompositeState {
low_quality_pinch_zoom: bool,
memory: &FrameMemory,
) -> Self {
// Since CompositorClipIndex is NonZeroUSize, we need to
// push a dummy entry in to this array.
let mut clips = memory.new_vec();
clips.push(CompositorClip {
rect: DeviceRect::zero(),
radius: BorderRadius::zero(),
});
CompositeState {
tiles: memory.new_vec(),
z_generator: ZBufferIdGenerator::new(max_depth_ids),
@@ -652,6 +679,7 @@ impl CompositeState {
picture_cache_debug: PictureCacheDebugInfo::new(),
transforms: memory.new_vec(),
low_quality_pinch_zoom,
clips,
}
}
@@ -674,6 +702,22 @@ impl CompositeState {
index
}
/// Register use of a clip for a picture cache tile and/or external surface
pub fn register_clip(
&mut self,
rect: DeviceRect,
radius: BorderRadius,
) -> CompositorClipIndex {
let index = CompositorClipIndex(NonZeroUsize::new(self.clips.len()).expect("bug"));
self.clips.push(CompositorClip {
rect,
radius,
});
index
}
/// Calculate the device-space rect of a local compositor surface rect
pub fn get_device_rect(
&self,
@@ -723,13 +767,44 @@ impl CompositeState {
transform.raster_to_device
}
/// Get the compositor clip
pub fn get_compositor_clip(
&self,
clip_index: CompositorClipIndex,
) -> &CompositorClip {
&self.clips[clip_index.0.get()]
}
/// Register an occluder during picture cache updates that can be
/// used during frame building to occlude tiles.
pub fn register_occluder(
&mut self,
z_id: ZBufferId,
rect: WorldRect,
compositor_clip: Option<CompositorClipIndex>,
) {
let rect = match compositor_clip {
Some(clip_index) => {
let clip = self.get_compositor_clip(clip_index);
let inner_rect = match extract_inner_rect_safe(
&clip.rect,
&clip.radius,
) {
Some(rect) => rect,
None => return,
};
match inner_rect.cast_unit().intersection(&rect) {
Some(rect) => rect,
None => return,
}
}
None => {
rect
}
};
let world_rect = rect.round().to_i32();
self.occluders.push(world_rect, z_id);
@@ -744,6 +819,7 @@ impl CompositeState {
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut FrameVec<DeferredResolve>,
clip_index: Option<CompositorClipIndex>,
) {
let clip_rect = external_surface
.clip_rect
@@ -815,6 +891,7 @@ impl CompositeState {
device_clip_rect: clip_rect,
z_id: external_surface.z_id,
transform_index: external_surface.transform_index,
clip_index,
};
// Add a surface descriptor for each compositor surface. For the Draw
@@ -879,6 +956,7 @@ impl CompositeState {
resource_cache,
gpu_cache,
deferred_resolves,
tile_cache.compositor_clip,
);
}
@@ -955,6 +1033,7 @@ impl CompositeState {
resource_cache,
gpu_cache,
deferred_resolves,
tile_cache.compositor_clip,
);
}
}

View File

@@ -4,7 +4,7 @@
use api::{AlphaType, PremultipliedColorF, YuvFormat, YuvRangedColorSpace};
use api::units::*;
use crate::composite::CompositeFeatures;
use crate::composite::{CompositeFeatures, CompositorClip};
use crate::segment::EdgeAaSegmentMask;
use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
use crate::gpu_cache::{GpuCacheAddress, GpuDataRequest};
@@ -14,7 +14,7 @@ use crate::render_task::RenderTaskAddress;
use crate::render_task_graph::RenderTaskId;
use crate::renderer::{ShaderColorMode, GpuBufferAddress};
use std::i32;
use crate::util::{TransformedRectKind, MatrixHelpers};
use crate::util::{MatrixHelpers, TransformedRectKind};
use glyph_rasterizer::SubpixelDirection;
use crate::util::{ScaleOffset, pack_as_float};
@@ -307,6 +307,10 @@ pub struct CompositeInstance {
// Whether to flip the x and y axis respectively, where 0.0 is no-flip and 1.0 is flip.
flip: (f32, f32),
// Optional rounded rect clip to apply during compositing
rounded_clip_rect: DeviceRect,
rounded_clip_radii: [f32; 4],
}
impl CompositeInstance {
@@ -315,8 +319,12 @@ impl CompositeInstance {
clip_rect: DeviceRect,
color: PremultipliedColorF,
flip: (bool, bool),
clip: Option<&CompositorClip>,
) -> Self {
let uv = TexelRect::new(0.0, 0.0, 1.0, 1.0);
let (rounded_clip_rect, rounded_clip_radii) = Self::vertex_clip_params(clip, rect);
CompositeInstance {
rect,
clip_rect,
@@ -327,6 +335,8 @@ impl CompositeInstance {
yuv_channel_bit_depth: 0.0,
uv_rects: [uv, uv, uv],
flip: (flip.0.into(), flip.1.into()),
rounded_clip_rect,
rounded_clip_radii,
}
}
@@ -337,7 +347,10 @@ impl CompositeInstance {
uv_rect: TexelRect,
normalized_uvs: bool,
flip: (bool, bool),
clip: Option<&CompositorClip>,
) -> Self {
let (rounded_clip_rect, rounded_clip_radii) = Self::vertex_clip_params(clip, rect);
let uv_type = match normalized_uvs {
true => UV_TYPE_NORMALIZED,
false => UV_TYPE_UNNORMALIZED,
@@ -352,6 +365,8 @@ impl CompositeInstance {
yuv_channel_bit_depth: 0.0,
uv_rects: [uv_rect, uv_rect, uv_rect],
flip: (flip.0.into(), flip.1.into()),
rounded_clip_rect,
rounded_clip_radii,
}
}
@@ -363,7 +378,11 @@ impl CompositeInstance {
yuv_channel_bit_depth: u32,
uv_rects: [TexelRect; 3],
flip: (bool, bool),
clip: Option<&CompositorClip>,
) -> Self {
let (rounded_clip_rect, rounded_clip_radii) = Self::vertex_clip_params(clip, rect);
CompositeInstance {
rect,
clip_rect,
@@ -374,6 +393,8 @@ impl CompositeInstance {
yuv_channel_bit_depth: pack_as_float(yuv_channel_bit_depth),
uv_rects,
flip: (flip.0.into(), flip.1.into()),
rounded_clip_rect,
rounded_clip_radii,
}
}
@@ -394,8 +415,48 @@ impl CompositeInstance {
features |= CompositeFeatures::NO_COLOR_MODULATION
}
// If all the clip radii are <= 0.0, then we don't need clip masking
if self.rounded_clip_radii.iter().all(|r| *r <= 0.0) {
features |= CompositeFeatures::NO_CLIP_MASK
}
features
}
// Returns the CompositeFeatures that can be used to composite
// this YUV instance.
pub fn get_yuv_features(&self) -> CompositeFeatures {
let mut features = CompositeFeatures::empty();
// If all the clip radii are <= 0.0, then we don't need clip masking
if self.rounded_clip_radii.iter().all(|r| *r <= 0.0) {
features |= CompositeFeatures::NO_CLIP_MASK
}
features
}
fn vertex_clip_params(
clip: Option<&CompositorClip>,
default_rect: DeviceRect,
) -> (DeviceRect, [f32; 4]) {
match clip {
Some(clip) => {
(
clip.rect.cast_unit(),
[
clip.radius.top_left.width,
clip.radius.bottom_left.width,
clip.radius.top_right.width,
clip.radius.bottom_right.width,
],
)
}
None => {
(default_rect, [0.0; 4])
}
}
}
}
/// Vertex format for issuing colored quads.

View File

@@ -94,7 +94,7 @@
//! blend the overlay tile (this is not always optimal right now, but will be
//! improved as a follow up).
use api::{FilterPrimitiveKind, MixBlendMode, PremultipliedColorF, SVGFE_GRAPH_MAX};
use api::{BorderRadius, ClipMode, FilterPrimitiveKind, MixBlendMode, PremultipliedColorF, SVGFE_GRAPH_MAX};
use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FilterOpGraphPictureBufferId, RasterSpace};
use api::{DebugFlags, ImageKey, ColorF, ColorU, PrimitiveFlags, SnapshotInfo};
use api::{ImageRendering, ColorDepth, YuvRangedColorSpace, YuvFormat, AlphaType};
@@ -102,10 +102,10 @@ use api::units::*;
use crate::prim_store::image::AdjustedImageSource;
use crate::{command_buffer::PrimitiveCommand, render_task_graph::RenderTaskGraphBuilder, renderer::GpuBufferBuilderF};
use crate::box_shadow::BLUR_SAMPLE_SCALE;
use crate::clip::{ClipStore, ClipChainInstance, ClipLeafId, ClipNodeId, ClipTreeBuilder};
use crate::clip::{ClipChainInstance, ClipItemKind, ClipLeafId, ClipNodeId, ClipSpaceConversion, ClipStore, ClipTreeBuilder};
use crate::profiler::{self, TransactionProfile};
use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace};
use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId, NativeTileId, CompositeTileSurface, tile_kind};
use crate::composite::{tile_kind, CompositeState, CompositeTileSurface, CompositorClipIndex, CompositorKind, NativeSurfaceId, NativeTileId};
use crate::composite::{ExternalSurfaceDescriptor, ExternalSurfaceDependency, CompositeTileDescriptor, CompositeTile};
use crate::composite::{CompositorTransformIndex, CompositorSurfaceKind};
use crate::debug_colors;
@@ -1824,6 +1824,8 @@ pub struct TileCacheInstance {
pub local_rect: PictureRect,
/// The local clip rect, from the shared clips of this picture.
pub local_clip_rect: PictureRect,
/// Registered clip in CompositeState for this picture cache
pub compositor_clip: Option<CompositorClipIndex>,
/// The screen rect, transformed to local picture space.
pub screen_rect_in_pic_space: PictureRect,
/// The surface index that this tile cache will be drawn into.
@@ -1960,6 +1962,7 @@ impl TileCacheInstance {
tile_bounds_p1: TileOffset::zero(),
local_rect: PictureRect::zero(),
local_clip_rect: PictureRect::zero(),
compositor_clip: None,
screen_rect_in_pic_space: PictureRect::zero(),
surface_index: SurfaceIndex(0),
background_color: params.background_color,
@@ -2206,9 +2209,72 @@ impl TileCacheInstance {
// Ensure that if the entire picture cache is clipped out, the local
// clip rect is zero. This makes sure we don't register any occluders
// that are actually off-screen.
self.local_clip_rect = clip_chain_instance.map_or(PictureRect::zero(), |clip_chain_instance| {
clip_chain_instance.pic_coverage_rect
});
self.local_clip_rect = PictureRect::zero();
self.compositor_clip = None;
if let Some(clip_chain) = clip_chain_instance {
self.local_clip_rect = clip_chain.pic_coverage_rect;
self.compositor_clip = if clip_chain.needs_mask {
let clip_instance = frame_state
.clip_store
.get_instance_from_range(&clip_chain.clips_range, 0);
let clip_node = &frame_state.data_stores.clip[clip_instance.handle];
let index = match clip_node.item.kind {
ClipItemKind::RoundedRectangle { rect, radius, mode } => {
assert_eq!(mode, ClipMode::Clip);
// Map the clip in to device space. We know from the shared
// clip creation logic it's in root coord system, so only a
// 2d axis-aligned transform can apply. For example, in the
// case of a pinch-zoom effect.
let map = ClipSpaceConversion::new(
frame_context.root_spatial_node_index,
clip_node.item.spatial_node_index,
frame_context.root_spatial_node_index,
frame_context.spatial_tree,
);
let (rect, radius) = match map {
ClipSpaceConversion::Local => {
(rect.cast_unit(), radius)
}
ClipSpaceConversion::ScaleOffset(scale_offset) => {
(
scale_offset.map_rect(&rect),
BorderRadius {
top_left: scale_offset.map_size(&radius.top_left),
top_right: scale_offset.map_size(&radius.top_right),
bottom_left: scale_offset.map_size(&radius.bottom_left),
bottom_right: scale_offset.map_size(&radius.bottom_right),
},
)
}
ClipSpaceConversion::Transform(..) => {
unreachable!();
}
};
frame_state.composite_state.register_clip(
rect,
radius,
)
}
_ => {
// The logic to check for shared clips excludes other mask
// clip types (box-shadow, image-mask) and ensures that the
// clip is in the root coord system (so rect clips can't
// produce a mask).
unreachable!();
}
};
Some(index)
} else {
None
};
}
}
// Advance the current frame ID counter for this picture cache (must be done
@@ -3950,6 +4016,7 @@ impl TileCacheInstance {
composite_state.register_occluder(
underlay.z_id,
world_surface_rect,
self.compositor_clip,
);
}
}
@@ -3964,6 +4031,7 @@ impl TileCacheInstance {
composite_state.register_occluder(
compositor_surface.descriptor.z_id,
world_surface_rect,
self.compositor_clip,
);
}
}
@@ -3991,6 +4059,7 @@ impl TileCacheInstance {
composite_state.register_occluder(
z_id_backdrop,
world_backdrop_rect,
self.compositor_clip,
);
}
}
@@ -5752,6 +5821,7 @@ impl PicturePrimitive {
device_clip_rect,
z_id: tile.z_id,
transform_index: tile_cache.transform_index,
clip_index: tile_cache.compositor_clip,
};
sub_slice.composite_tiles.push(composite_tile);

View File

@@ -1119,8 +1119,8 @@ pub enum QuadTileKind {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[derive(Copy, Clone, Debug)]
pub struct QuadTileInfo {
rect: LayoutRect,
kind: QuadTileKind,
pub rect: LayoutRect,
pub kind: QuadTileKind,
}
impl Default for QuadTileInfo {

View File

@@ -34,7 +34,7 @@
//! up the scissor, are accepting already transformed coordinates, which we can get by
//! calling `DrawTarget::to_framebuffer_rect`
use api::{ColorF, ColorU, MixBlendMode};
use api::{ClipMode, ColorF, ColorU, MixBlendMode};
use api::{DocumentId, Epoch, ExternalImageHandler, RenderReasons};
#[cfg(feature = "replay")]
use api::ExternalImageId;
@@ -58,6 +58,7 @@ use crate::composite::{CompositeState, CompositeTileSurface, CompositorInputLaye
use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeFeatures, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData};
use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation};
use crate::composite::TileKind;
use crate::segment::SegmentBuilder;
use crate::{debug_colors, CompositorInputConfig, CompositorSurfaceUsage};
use crate::device::{DepthFunction, Device, DrawTarget, ExternalTexture, GpuFrameId, UploadPBOPool};
use crate::device::{ReadTarget, ShaderError, Texture, TextureFilter, TextureFlags, TextureSlot, Texel};
@@ -265,11 +266,21 @@ const GPU_TAG_COMPOSITE: GpuProfileTag = GpuProfileTag {
color: debug_colors::TOMATO,
};
// Key used when adding compositing tiles to the occlusion tracker.
// Since an entire tile may have a mask, but we may segment that in
// to masked and non-masked regions, we need to track which of the
// occlusion tracker outputs need a mask
#[derive(Debug, Copy, Clone)]
struct OcclusionItemKey {
tile_index: usize,
needs_mask: bool,
}
// Defines the content that we will draw to a given swapchain / layer, calculated
// after occlusion culling.
struct SwapChainLayer {
occlusion: occlusion::FrontToBackBuilder<usize>,
clear_tiles: Vec<occlusion::Item<usize>>,
occlusion: occlusion::FrontToBackBuilder<OcclusionItemKey>,
clear_tiles: Vec<occlusion::Item<OcclusionItemKey>>,
}
/// The clear color used for the texture cache when the debug display is enabled.
@@ -3068,21 +3079,6 @@ impl Renderer {
ResolvedExternalSurfaceColorData::Yuv{
ref planes, color_space, format, channel_bit_depth, .. } => {
// Bind an appropriate YUV shader for the texture format kind
self.shaders
.borrow_mut()
.get_composite_shader(
CompositeSurfaceFormat::Yuv,
surface.image_buffer_kind,
CompositeFeatures::empty(),
).bind(
&mut self.device,
&projection,
None,
&mut self.renderer_errors,
&mut self.profile,
);
let textures = BatchTextures::composite_yuv(
planes[0].texture,
planes[1].texture,
@@ -3110,17 +3106,16 @@ impl Renderer {
channel_bit_depth,
uv_rects,
(false, false),
None,
);
( textures, instance )
},
ResolvedExternalSurfaceColorData::Rgb{ ref plane, .. } => {
// Bind an appropriate YUV shader for the texture format kind
self.shaders
.borrow_mut()
.get_composite_shader(
CompositeSurfaceFormat::Rgba,
CompositeSurfaceFormat::Yuv,
surface.image_buffer_kind,
CompositeFeatures::empty(),
instance.get_yuv_features(),
).bind(
&mut self.device,
&projection,
@@ -3128,7 +3123,10 @@ impl Renderer {
&mut self.renderer_errors,
&mut self.profile,
);
( textures, instance )
},
ResolvedExternalSurfaceColorData::Rgb{ ref plane, .. } => {
let textures = BatchTextures::composite_rgb(plane.texture);
let uv_rect = self.texture_resolver.get_uv_rect(&textures.input.colors[0], plane.uv_rect);
let instance = CompositeInstance::new_rgb(
@@ -3138,7 +3136,23 @@ impl Renderer {
uv_rect,
plane.texture.uses_normalized_uvs(),
(false, false),
None,
);
let features = instance.get_rgb_features();
self.shaders
.borrow_mut()
.get_composite_shader(
CompositeSurfaceFormat::Rgba,
surface.image_buffer_kind,
features,
).bind(
&mut self.device,
&projection,
None,
&mut self.renderer_errors,
&mut self.profile,
);
( textures, instance )
},
@@ -3161,7 +3175,7 @@ impl Renderer {
}
/// Draw a list of tiles to the framebuffer
fn draw_tile_list<'a, I: Iterator<Item = &'a occlusion::Item<usize>>>(
fn draw_tile_list<'a, I: Iterator<Item = &'a occlusion::Item<OcclusionItemKey>>>(
&mut self,
tiles_iter: I,
composite_state: &CompositeState,
@@ -3193,13 +3207,21 @@ impl Renderer {
);
for item in tiles_iter {
let tile = &composite_state.tiles[item.key];
let tile = &composite_state.tiles[item.key.tile_index];
let clip_rect = item.rectangle;
let tile_rect = composite_state.get_device_rect(&tile.local_rect, tile.transform_index);
let transform = composite_state.get_device_transform(tile.transform_index);
let flip = (transform.scale.x < 0.0, transform.scale.y < 0.0);
let clip = if item.key.needs_mask {
tile.clip_index.map(|index| {
composite_state.get_compositor_clip(index)
})
} else {
None
};
// Work out the draw params based on the tile surface
let (instance, textures, shader_params) = match tile.surface {
CompositeTileSurface::Color { color } => {
@@ -3210,6 +3232,7 @@ impl Renderer {
clip_rect,
color.premultiplied(),
flip,
clip,
);
let features = instance.get_rgb_features();
(
@@ -3224,6 +3247,7 @@ impl Renderer {
clip_rect,
PremultipliedColorF::WHITE,
flip,
clip,
);
let features = instance.get_rgb_features();
(
@@ -3259,21 +3283,25 @@ impl Renderer {
self.texture_resolver.get_uv_rect(&textures.input.colors[2], planes[2].uv_rect),
];
let instance = CompositeInstance::new_yuv(
tile_rect,
clip_rect,
color_space,
format,
channel_bit_depth,
uv_rects,
flip,
clip,
);
let features = instance.get_yuv_features();
(
CompositeInstance::new_yuv(
tile_rect,
clip_rect,
color_space,
format,
channel_bit_depth,
uv_rects,
flip,
),
instance,
textures,
(
CompositeSurfaceFormat::Yuv,
surface.image_buffer_kind,
CompositeFeatures::empty(),
features,
None
),
)
@@ -3287,6 +3315,7 @@ impl Renderer {
uv_rect,
plane.texture.uses_normalized_uvs(),
flip,
clip,
);
let features = instance.get_rgb_features();
(
@@ -3310,6 +3339,7 @@ impl Renderer {
clip_rect,
PremultipliedColorF::BLACK,
flip,
clip,
);
let features = instance.get_rgb_features();
(
@@ -3503,6 +3533,7 @@ impl Renderer {
let mut input_layers: Vec<CompositorInputLayer> = Vec::new();
let mut swapchain_layers = Vec::new();
let cap = composite_state.tiles.len();
let mut segment_builder = SegmentBuilder::new();
// NOTE: Tiles here are being iterated in front-to-back order by
// z-id, due to the sort in composite_state.end_frame()
@@ -3646,20 +3677,48 @@ impl Renderer {
// Clear tiles overwrite whatever is under them, so they are treated as opaque.
match tile.kind {
TileKind::Opaque => {
// Store (index of tile, index of layer) so we can segment them below
layer.occlusion.add(&rect, true, idx);
}
TileKind::Alpha => {
// Store (index of tile, index of layer) so we can segment them below
layer.occlusion.add(&rect, false, idx);
TileKind::Opaque | TileKind::Alpha => {
let is_opaque = tile.kind != TileKind::Alpha;
match tile.clip_index {
Some(clip_index) => {
let clip = composite_state.get_compositor_clip(clip_index);
// TODO(gw): Make segment builder generic on unit to avoid casts below.
segment_builder.initialize(
rect.cast_unit(),
None,
rect.cast_unit(),
);
segment_builder.push_clip_rect(
clip.rect.cast_unit(),
Some(clip.radius),
ClipMode::Clip,
);
segment_builder.build(|segment| {
let key = OcclusionItemKey { tile_index: idx, needs_mask: segment.has_mask };
layer. occlusion.add(
&segment.rect.cast_unit(),
is_opaque && !segment.has_mask,
key,
);
});
}
None => {
layer.occlusion.add(&rect, is_opaque, OcclusionItemKey {
tile_index: idx,
needs_mask: false,
});
}
}
}
TileKind::Clear => {
// Clear tiles are specific to how we render the window buttons on
// Windows 8. They clobber what's under them so they can be treated as opaque,
// but require a different blend state so they will be rendered after the opaque
// tiles and before transparent ones.
layer.clear_tiles.push(occlusion::Item { rectangle: rect, key: idx });
layer.clear_tiles.push(occlusion::Item { rectangle: rect, key: OcclusionItemKey { tile_index: idx, needs_mask: false } });
}
}
}

View File

@@ -1382,7 +1382,8 @@ pub struct CompositorShaders {
// or color modulation.
rgba_fast_path: Vec<Option<LazilyCompiledShader>>,
// The same set of composite shaders but with WR_FEATURE_YUV added.
yuv: Vec<Option<LazilyCompiledShader>>,
yuv_clip: Vec<Option<LazilyCompiledShader>>,
yuv_fast: Vec<Option<LazilyCompiledShader>>,
}
impl CompositorShaders {
@@ -1395,12 +1396,14 @@ impl CompositorShaders {
// so use a dummy one.
let mut profile = TransactionProfile::new();
let mut yuv_features = Vec::new();
let mut yuv_clip_features = Vec::new();
let mut yuv_fast_features = Vec::new();
let mut rgba_features = Vec::new();
let mut fast_path_features = Vec::new();
let mut rgba = Vec::new();
let mut rgba_fast_path = Vec::new();
let mut yuv = Vec::new();
let mut yuv_clip = Vec::new();
let mut yuv_fast = Vec::new();
let texture_external_version = if device.get_capabilities().supports_image_external_essl3 {
TextureExternalVersion::ESSL3
@@ -1412,7 +1415,8 @@ impl CompositorShaders {
let shader_list = get_shader_features(feature_flags);
for _ in 0..IMAGE_BUFFER_KINDS.len() {
yuv.push(None);
yuv_clip.push(None);
yuv_fast.push(None);
rgba.push(None);
rgba_fast_path.push(None);
}
@@ -1422,7 +1426,9 @@ impl CompositorShaders {
continue;
}
yuv_features.push("YUV");
yuv_clip_features.push("YUV");
yuv_fast_features.push("YUV");
yuv_fast_features.push("FAST_PATH");
fast_path_features.push("FAST_PATH");
let index = Self::get_shader_index(*image_buffer_kind);
@@ -1432,7 +1438,8 @@ impl CompositorShaders {
texture_external_version,
);
if feature_string != "" {
yuv_features.push(feature_string);
yuv_clip_features.push(feature_string);
yuv_fast_features.push(feature_string);
rgba_features.push(feature_string);
fast_path_features.push(feature_string);
}
@@ -1441,10 +1448,20 @@ impl CompositorShaders {
if *image_buffer_kind != ImageBufferKind::TextureExternal ||
texture_external_version == TextureExternalVersion::ESSL3 {
yuv[index] = Some(LazilyCompiledShader::new(
yuv_clip[index] = Some(LazilyCompiledShader::new(
ShaderKind::Composite,
"composite",
&yuv_features,
&yuv_clip_features,
device,
precache_flags,
&shader_list,
&mut profile,
)?);
yuv_fast[index] = Some(LazilyCompiledShader::new(
ShaderKind::Composite,
"composite",
&yuv_fast_features,
device,
precache_flags,
&shader_list,
@@ -1472,7 +1489,8 @@ impl CompositorShaders {
&mut profile,
)?);
yuv_features.clear();
yuv_fast_features.clear();
yuv_clip_features.clear();
rgba_features.clear();
fast_path_features.clear();
}
@@ -1480,7 +1498,8 @@ impl CompositorShaders {
Ok(CompositorShaders {
rgba,
rgba_fast_path,
yuv,
yuv_clip,
yuv_fast,
})
}
@@ -1494,6 +1513,7 @@ impl CompositorShaders {
CompositeSurfaceFormat::Rgba => {
if features.contains(CompositeFeatures::NO_UV_CLAMP)
&& features.contains(CompositeFeatures::NO_COLOR_MODULATION)
&& features.contains(CompositeFeatures::NO_CLIP_MASK)
{
let shader_index = Self::get_shader_index(buffer_kind);
self.rgba_fast_path[shader_index]
@@ -1508,9 +1528,15 @@ impl CompositorShaders {
}
CompositeSurfaceFormat::Yuv => {
let shader_index = Self::get_shader_index(buffer_kind);
self.yuv[shader_index]
.as_mut()
.expect("bug: unsupported yuv shader requested")
if features.contains(CompositeFeatures::NO_CLIP_MASK) {
self.yuv_fast[shader_index]
.as_mut()
.expect("bug: unsupported yuv shader requested")
} else {
self.yuv_clip[shader_index]
.as_mut()
.expect("bug: unsupported yuv shader requested")
}
}
}
}
@@ -1530,7 +1556,12 @@ impl CompositorShaders {
shader.deinit(device);
}
}
for shader in self.yuv.drain(..) {
for shader in self.yuv_clip.drain(..) {
if let Some(shader) = shader {
shader.deinit(device);
}
}
for shader in self.yuv_fast.drain(..) {
if let Some(shader) = shader {
shader.deinit(device);
}

View File

@@ -776,6 +776,16 @@ pub mod desc {
count: 2,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aDeviceRoundedClipRect",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aDeviceRoundedClipRadii",
count: 4,
kind: VertexAttributeKind::F32,
},
],
};

View File

@@ -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::{ColorF, DebugFlags, PrimitiveFlags, QualitySettings, RasterSpace, ClipId};
use api::{BorderRadius, ClipId, ClipMode, ColorF, DebugFlags, PrimitiveFlags, QualitySettings, RasterSpace};
use api::units::*;
use crate::clip::{ClipItemKeyKind, ClipNodeId, ClipTreeBuilder};
use crate::frame_builder::FrameBuilderConfig;
@@ -561,19 +561,26 @@ fn create_tile_cache(
let node_valid = if is_rcs {
match clip_node_data.key.kind {
ClipItemKeyKind::BoxShadow(..) | ClipItemKeyKind::ImageMask(..) => {
ClipItemKeyKind::BoxShadow(..) |
ClipItemKeyKind::ImageMask(..) |
ClipItemKeyKind::Rectangle(_, ClipMode::ClipOut) |
ClipItemKeyKind::RoundedRectangle(_, _, ClipMode::ClipOut) => {
// Has a box-shadow / image-mask, we can't handle this as a shared clip
false
}
ClipItemKeyKind::RoundedRectangle(..) => {
rounded_rect_count += 1;
ClipItemKeyKind::RoundedRectangle(rect, radius, ClipMode::Clip) => {
// The shader and CoreAnimation rely on certain constraints such
// as uniform radii to be able to apply the clip during compositing.
if BorderRadius::from(radius).can_use_fast_path_in(&rect.into()) {
rounded_rect_count += 1;
// TODO(gw): This initial patch retains existing behavior by not
// allowing a rounded-rect clip to be part of the shared
// clip. Follow up patch in this series will relax this.
false
// TODO(gw): Enable this once also supported by native and swgl compositors
false
} else {
false
}
}
ClipItemKeyKind::Rectangle(..) => {
ClipItemKeyKind::Rectangle(_, ClipMode::Clip) => {
// We can apply multiple (via combining) axis-aligned rectangle
// clips to the shared compositing clip.
true

View File

@@ -209,6 +209,14 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures {
}
list.add("FAST_PATH");
composite_features.push(list.finish());
// YUV shaders are not compatible with ESSL1
if *texture_type == "TEXTURE_EXTERNAL_ESSL1" {
continue;
}
list.add("YUV");
composite_features.push(list.finish());
}
shaders.insert("composite", composite_features);

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@@ -0,0 +1,7 @@
color_targets(3) == rounded-corners-1.yaml rounded-corners-1-ref.yaml
# color_targets(2) == rounded-corners-2.yaml rounded-corners-2-ref.yaml
== rounded-corners-3.yaml rounded-corners-3-ref.yaml
== rounded-corners-4.yaml rounded-corners-4-ref.yaml
# fuzzy(2,106) == rounded-yuv-surface.yaml rounded-yuv-surface-ref.yaml
== rounded-rgb-surface.yaml rounded-rgb-surface-ref.yaml
== tile-occlusion.yaml tile-occlusion-ref.yaml

View File

@@ -0,0 +1,13 @@
---
root:
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: 32
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]

View File

@@ -0,0 +1,25 @@
# Since there is no clip-chain shared between the two prims, the rounded
# clip will be applied to the primitive, causing allocation of a mask surface.
# The second prim is clipped out to make it match the reference.
---
root:
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: 32
- type: clip
id: 3
bounds: [300, 50, 0, 0]
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]
- type: rect
bounds: 300 50 200 200
color: red
clip-chain: [3]

View File

@@ -0,0 +1,13 @@
---
root:
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: 32
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]

View File

@@ -0,0 +1,21 @@
# Since the clip-chain is shared between the two prims, the rounded
# clip will be applied to the tile cache, so no mask surface will be allocated.
# The second prim is clipped out to make it match the reference.
---
root:
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: 32
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]
- type: rect
bounds: 300 50 200 200
color: red
clip-chain: [2]

View File

@@ -0,0 +1,22 @@
---
root:
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: [25, 50, 100, 75]
- type: clip
id: 3
bounds: [300, 50, 0, 0]
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]
- type: rect
bounds: 300 50 200 200
color: red
clip-chain: [3]

View File

@@ -0,0 +1,24 @@
# When the clip chains are shared and handled when compositing the picture cache
# slice, ensure that the border radii are passed in the correct order to the shader.
---
root:
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: [25, 50, 100, 75]
- type: clip
id: 3
bounds: [300, 50, 0, 0]
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]
- type: rect
bounds: 300 50 200 200
color: red
clip-chain: [2]

View File

@@ -0,0 +1,25 @@
---
root:
items:
- type: stacking-context
transform: scale(0.5)
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: 100
- type: clip
id: 3
bounds: [300, 50, 0, 0]
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]
- type: rect
bounds: 300 50 200 200
color: red
clip-chain: [3]

View File

@@ -0,0 +1,27 @@
# Ensure that shared (compositor applied) clip correctly handles transform from
# (axis-aligned) local coordinate system to device coordinate system
---
root:
items:
- type: stacking-context
transform: scale(0.5)
items:
- type: clip
id: 2
complex:
- rect: [50, 50, 200, 200]
radius: 100
- type: clip
id: 3
bounds: [300, 50, 0, 0]
- type: rect
bounds: 50 50 200 200
color: red
clip-chain: [2]
- type: rect
bounds: 300 50 200 200
color: red
clip-chain: [2]

View File

@@ -0,0 +1,12 @@
---
root:
items:
- type: clip
id: 2
complex:
- rect: [100, 100, 200, 200]
radius: 32
- bounds: [100, 100, 200, 200]
image: checkerboard(4, 16, 12)
clip-chain: [2]

View File

@@ -0,0 +1,15 @@
# Ensure that a RGB compositor surface is correctly clipped when
# compositing with a shared rounded clip on the tile cache.
---
root:
items:
- type: clip
id: 2
complex:
- rect: [100, 100, 200, 200]
radius: 32
- bounds: [100, 100, 200, 200]
image: checkerboard(4, 16, 12)
clip-chain: [2]
prefer-compositor-surface: true

View File

@@ -0,0 +1,16 @@
---
root:
items:
- type: clip
id: 2
complex:
- rect: [100, 100, 299, 299]
radius: 32
- type: yuv-image
format: planar
src-y: barn-y.png
src-u: barn-u.png
src-v: barn-v.png
bounds: [100, 100, 299, 299]
clip-chain: [2]

View File

@@ -0,0 +1,19 @@
# Ensure that a YUV compositor surface is correctly clipped when
# compositing with a shared rounded clip on the tile cache.
---
root:
items:
- type: clip
id: 2
complex:
- rect: [100, 100, 299, 299]
radius: 32
- type: yuv-image
format: planar
src-y: barn-y.png
src-u: barn-u.png
src-v: barn-v.png
bounds: [100, 100, 299, 299]
clip-chain: [2]
prefer-compositor-surface: true

View File

@@ -0,0 +1,17 @@
---
root:
items:
- type: clip
id: 2
complex:
- rect: [100, 100, 200, 200]
radius: 64
- type: rect
bounds: [100, 100, 200, 200]
color: green
- type: rect
bounds: [100, 100, 200, 200]
color: red
clip-chain: [2]

View File

@@ -0,0 +1,25 @@
# Ensure that a compositor surface with a rounded clip applied
# doesn't incorrectly occlude tile(s) behind it.
---
root:
items:
- type: clip
id: 2
complex:
- rect: [100, 100, 200, 200]
radius: 64
- type: rect
bounds: [100, 100, 200, 200]
color: green
-
type: "scroll-frame"
id: 3
bounds: [0, 0, 300, 300]
- type: rect
spatial-id: 3
bounds: [100, 100, 200, 200]
color: red
clip-chain: [2]

View File

@@ -4,6 +4,7 @@ include blend/reftest.list
include border/reftest.list
include boxshadow/reftest.list
include clip/reftest.list
include compositor/reftest.list
include compositor-surface/reftest.list
include crash/reftest.list
include filters/reftest.list