зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1628175 - WebGL is drawn into the picture cache and then onto the screen r=gw
Part 1 - support RGB external surfaces for promotion to compositor surfaces; add new shader permutations to handle all buffer kinds. Set the promotion flag when the pixel format has no alpha, or when the texture provider can guarantee all-solid alpha values. Differential Revision: https://phabricator.services.mozilla.com/D71120
This commit is contained in:
Родитель
0f8bea4c9f
Коммит
b91a3eabe0
|
@ -79,9 +79,13 @@ enum class TextureFlags : uint32_t {
|
|||
BLOCKING_READ_LOCK = 1 << 16,
|
||||
// Keep TextureClient alive when host side is used
|
||||
WAIT_HOST_USAGE_END = 1 << 17,
|
||||
// The texture is guaranteed to have alpha 1.0 everywhere; some backends
|
||||
// have trouble with RGBX/BGRX formats, so we use RGBA/BGRA but set this
|
||||
// hint when we know alpha is opaque (eg. WebGL)
|
||||
IS_OPAQUE = 1 << 18,
|
||||
|
||||
// OR union of all valid bits
|
||||
ALL_BITS = (1 << 18) - 1,
|
||||
ALL_BITS = (1 << 19) - 1,
|
||||
// the default flags
|
||||
DEFAULT = NO_FLAGS
|
||||
};
|
||||
|
|
|
@ -51,6 +51,9 @@ void ShareableCanvasRenderer::Initialize(const CanvasInitializeData& aData) {
|
|||
mFlags |= TextureFlags::NON_PREMULTIPLIED;
|
||||
}
|
||||
|
||||
if (!aData.mHasAlpha) {
|
||||
mFlags |= TextureFlags::IS_OPAQUE;
|
||||
}
|
||||
UniquePtr<gl::SurfaceFactory> factory =
|
||||
gl::GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags);
|
||||
if (factory) {
|
||||
|
|
|
@ -22,6 +22,10 @@ bool ClientCanvasRenderer::CreateCompositable() {
|
|||
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
}
|
||||
|
||||
if (IsOpaque()) {
|
||||
flags |= TextureFlags::IS_OPAQUE;
|
||||
}
|
||||
|
||||
if (!mIsAlphaPremultiplied) {
|
||||
flags |= TextureFlags::NON_PREMULTIPLIED;
|
||||
}
|
||||
|
|
|
@ -396,9 +396,14 @@ void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
|
|||
if (aPipeline->mUseExternalImage) {
|
||||
MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
|
||||
Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
|
||||
bool prefer_compositor_surface =
|
||||
IsOpaque(aPipeline->mCurrentTexture->GetFormat()) ||
|
||||
bool(aPipeline->mCurrentTexture->GetFlags() &
|
||||
TextureFlags::IS_OPAQUE);
|
||||
aPipeline->mCurrentTexture->PushDisplayItems(
|
||||
builder, wr::ToLayoutRect(rect), wr::ToLayoutRect(rect),
|
||||
aPipeline->mFilter, range_keys, /* aPreferCompositorSurface */ true);
|
||||
aPipeline->mFilter, range_keys,
|
||||
/* aPreferCompositorSurface */ prefer_compositor_surface);
|
||||
HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture);
|
||||
} else {
|
||||
MOZ_ASSERT(keys.Length() == 1);
|
||||
|
|
|
@ -40,6 +40,10 @@ bool WebRenderCanvasRendererAsync::CreateCompositable() {
|
|||
flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
}
|
||||
|
||||
if (IsOpaque()) {
|
||||
flags |= TextureFlags::IS_OPAQUE;
|
||||
}
|
||||
|
||||
if (!mIsAlphaPremultiplied) {
|
||||
flags |= TextureFlags::NON_PREMULTIPLIED;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ varying vec2 vUv;
|
|||
#endif
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
// CPU side data is in CompositeInstance (gpu_types.rs) and is
|
||||
// converted to GPU data using desc::COMPOSITE (renderer.rs) by
|
||||
// filling vaos.composite_vao with VertexArrayKind::Composite.
|
||||
PER_INSTANCE in vec4 aDeviceRect;
|
||||
PER_INSTANCE in vec4 aDeviceClipRect;
|
||||
PER_INSTANCE in vec4 aColor;
|
||||
|
@ -108,9 +111,13 @@ void main(void) {
|
|||
);
|
||||
#else
|
||||
// The color is just the texture sample modulated by a supplied color
|
||||
vec4 texel = textureLod(sColor0, vec3(vUv, vLayer), 0.0);
|
||||
# if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_2D) || defined(WR_FEATURE_TEXTURE_RECT)
|
||||
vec4 texel = TEX_SAMPLE(sColor0, vec3(vUv, vLayer));
|
||||
# else
|
||||
vec4 texel = textureLod(sColor0, vec3(vUv, vLayer), 0.0);
|
||||
# endif
|
||||
vec4 color = vColor * texel;
|
||||
#endif
|
||||
write_output(color);
|
||||
write_output(color);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSourc
|
|||
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
|
||||
use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask};
|
||||
use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
|
||||
use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveVisibilityFlags};
|
||||
use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveVisibility, PrimitiveVisibilityFlags};
|
||||
use crate::prim_store::{VECS_PER_SEGMENT, SpaceMapper};
|
||||
use crate::prim_store::image::ImageSource;
|
||||
use crate::render_target::RenderTargetContext;
|
||||
|
@ -724,6 +724,73 @@ impl BatchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
// If an image is being drawn as a compositor surface, we don't want
|
||||
// to draw the surface itself into the tile. Instead, we draw a transparent
|
||||
// rectangle that writes to the z-buffer where this compositor surface is.
|
||||
// That ensures we 'cut out' the part of the tile that has the compositor
|
||||
// surface on it, allowing us to draw this tile as an overlay on top of
|
||||
// the compositor surface.
|
||||
// TODO(gw): There's a slight performance cost to doing this cutout rectangle
|
||||
// if we end up not needing to use overlay mode. Consider skipping
|
||||
// the cutout completely in this path.
|
||||
fn emit_placeholder(
|
||||
&mut self,
|
||||
prim_rect: LayoutRect,
|
||||
prim_info: &PrimitiveVisibility,
|
||||
z_id: ZBufferId,
|
||||
transform_id: TransformPaletteId,
|
||||
batch_features: BatchFeatures,
|
||||
ctx: &RenderTargetContext,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &RenderTaskGraph,
|
||||
prim_headers: &mut PrimitiveHeaders,
|
||||
) {
|
||||
let batch_params = BrushBatchParameters::shared(
|
||||
BrushBatchKind::Solid,
|
||||
BatchTextures::no_texture(),
|
||||
[get_shader_opacity(0.0), 0, 0, 0],
|
||||
0,
|
||||
);
|
||||
|
||||
let prim_cache_address = gpu_cache.get_address(
|
||||
&ctx.globals.default_transparent_rect_handle,
|
||||
);
|
||||
|
||||
let prim_header = PrimitiveHeader {
|
||||
local_rect: prim_rect,
|
||||
local_clip_rect: prim_info.combined_local_clip_rect,
|
||||
specific_prim_address: prim_cache_address,
|
||||
transform_id,
|
||||
};
|
||||
|
||||
let prim_header_index = prim_headers.push(
|
||||
&prim_header,
|
||||
z_id,
|
||||
batch_params.prim_user_data,
|
||||
);
|
||||
|
||||
let bounding_rect = &prim_info.clip_chain.pic_clip_rect;
|
||||
let transform_kind = transform_id.transform_kind();
|
||||
let prim_vis_mask = prim_info.visibility_mask;
|
||||
|
||||
self.add_segmented_prim_to_batch(
|
||||
None,
|
||||
PrimitiveOpacity::translucent(),
|
||||
&batch_params,
|
||||
BlendMode::None,
|
||||
BlendMode::None,
|
||||
batch_features,
|
||||
prim_header_index,
|
||||
bounding_rect,
|
||||
transform_kind,
|
||||
render_tasks,
|
||||
z_id,
|
||||
prim_info.clip_task_index,
|
||||
prim_vis_mask,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
|
||||
// Adds a primitive to a batch.
|
||||
// It can recursively call itself in some situations, for
|
||||
// example if it encounters a picture where the items
|
||||
|
@ -1923,57 +1990,16 @@ impl BatchBuilder {
|
|||
);
|
||||
}
|
||||
PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, is_compositor_surface, .. } => {
|
||||
// If this YUV image is being drawn as a compositor surface, we don't want
|
||||
// to draw the YUV surface itself into the tile. Instead, we draw a transparent
|
||||
// rectangle that writes to the z-buffer where this compositor surface is.
|
||||
// That ensures we 'cut out' the part of the tile that has the compositor
|
||||
// surface on it, allowing us to draw this tile as an overlay on top of
|
||||
// the compositor surface.
|
||||
// TODO(gw): There's a slight performance cost to doing this cutout rectangle
|
||||
// if we end up not needing to use overlay mode. Consider skipping
|
||||
// the cutout completely in this path.
|
||||
if is_compositor_surface {
|
||||
let batch_params = BrushBatchParameters::shared(
|
||||
BrushBatchKind::Solid,
|
||||
BatchTextures::no_texture(),
|
||||
[get_shader_opacity(0.0), 0, 0, 0],
|
||||
0,
|
||||
);
|
||||
|
||||
let prim_cache_address = gpu_cache.get_address(
|
||||
&ctx.globals.default_transparent_rect_handle,
|
||||
);
|
||||
|
||||
let prim_header = PrimitiveHeader {
|
||||
local_rect: prim_rect,
|
||||
local_clip_rect: prim_info.combined_local_clip_rect,
|
||||
specific_prim_address: prim_cache_address,
|
||||
transform_id,
|
||||
};
|
||||
|
||||
let prim_header_index = prim_headers.push(
|
||||
&prim_header,
|
||||
z_id,
|
||||
batch_params.prim_user_data,
|
||||
);
|
||||
|
||||
self.add_segmented_prim_to_batch(
|
||||
None,
|
||||
PrimitiveOpacity::translucent(),
|
||||
&batch_params,
|
||||
BlendMode::None,
|
||||
BlendMode::None,
|
||||
batch_features,
|
||||
prim_header_index,
|
||||
bounding_rect,
|
||||
transform_kind,
|
||||
render_tasks,
|
||||
z_id,
|
||||
prim_info.clip_task_index,
|
||||
prim_vis_mask,
|
||||
ctx,
|
||||
);
|
||||
|
||||
self.emit_placeholder(prim_rect,
|
||||
prim_info,
|
||||
z_id,
|
||||
transform_id,
|
||||
batch_features,
|
||||
ctx,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
prim_headers);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2086,7 +2112,19 @@ impl BatchBuilder {
|
|||
ctx,
|
||||
);
|
||||
}
|
||||
PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => {
|
||||
PrimitiveInstanceKind::Image { data_handle, image_instance_index, is_compositor_surface, .. } => {
|
||||
if is_compositor_surface {
|
||||
self.emit_placeholder(prim_rect,
|
||||
prim_info,
|
||||
z_id,
|
||||
transform_id,
|
||||
batch_features,
|
||||
ctx,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
prim_headers);
|
||||
return;
|
||||
}
|
||||
let image_data = &ctx.data_stores.image[data_handle].kind;
|
||||
let common_data = &ctx.data_stores.image[data_handle].common;
|
||||
let image_instance = &ctx.prim_store.images[image_instance_index];
|
||||
|
|
|
@ -87,6 +87,18 @@ pub struct CompositeTile {
|
|||
pub z_id: ZBufferId,
|
||||
}
|
||||
|
||||
pub enum ExternalSurfaceDependency {
|
||||
Yuv {
|
||||
image_dependencies: [ImageDependency; 3],
|
||||
color_space: YuvColorSpace,
|
||||
format: YuvFormat,
|
||||
rescale: f32,
|
||||
},
|
||||
Rgb {
|
||||
image_dependency: ImageDependency,
|
||||
},
|
||||
}
|
||||
|
||||
/// Describes information about drawing a primitive as a compositor surface.
|
||||
/// For now, we support only YUV images as compositor surfaces, but in future
|
||||
/// this will also support RGBA images.
|
||||
|
@ -96,12 +108,9 @@ pub struct ExternalSurfaceDescriptor {
|
|||
pub device_rect: DeviceRect,
|
||||
pub local_clip_rect: PictureRect,
|
||||
pub clip_rect: DeviceRect,
|
||||
pub image_dependencies: [ImageDependency; 3],
|
||||
pub image_rendering: ImageRendering,
|
||||
pub yuv_color_space: YuvColorSpace,
|
||||
pub yuv_format: YuvFormat,
|
||||
pub yuv_rescale: f32,
|
||||
pub z_id: ZBufferId,
|
||||
pub dependency: ExternalSurfaceDependency,
|
||||
/// If native compositing is enabled, the native compositor surface handle.
|
||||
/// Otherwise, this will be None
|
||||
pub native_surface_id: Option<NativeSurfaceId>,
|
||||
|
@ -110,18 +119,19 @@ pub struct ExternalSurfaceDescriptor {
|
|||
pub update_params: Option<DeviceIntSize>,
|
||||
}
|
||||
|
||||
/// Information about a plane in a YUV surface.
|
||||
/// Information about a plane in a YUV or RGB surface.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct YuvPlaneDescriptor {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ExternalPlaneDescriptor {
|
||||
pub texture: TextureSource,
|
||||
pub texture_layer: i32,
|
||||
pub uv_rect: TexelRect,
|
||||
}
|
||||
|
||||
impl YuvPlaneDescriptor {
|
||||
impl ExternalPlaneDescriptor {
|
||||
fn invalid() -> Self {
|
||||
YuvPlaneDescriptor {
|
||||
ExternalPlaneDescriptor {
|
||||
texture: TextureSource::Invalid,
|
||||
texture_layer: 0,
|
||||
uv_rect: TexelRect::invalid(),
|
||||
|
@ -134,6 +144,23 @@ impl YuvPlaneDescriptor {
|
|||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ResolvedExternalSurfaceIndex(pub usize);
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum ResolvedExternalSurfaceColorData {
|
||||
Yuv {
|
||||
// YUV specific information
|
||||
image_dependencies: [ImageDependency; 3],
|
||||
planes: [ExternalPlaneDescriptor; 3],
|
||||
color_space: YuvColorSpace,
|
||||
format: YuvFormat,
|
||||
rescale: f32,
|
||||
},
|
||||
Rgb {
|
||||
image_dependency: ImageDependency,
|
||||
plane: ExternalPlaneDescriptor,
|
||||
},
|
||||
}
|
||||
|
||||
/// An ExternalSurfaceDescriptor that has had image keys
|
||||
/// resolved to texture handles. This contains all the
|
||||
/// information that the compositor step in renderer
|
||||
|
@ -141,14 +168,8 @@ pub struct ResolvedExternalSurfaceIndex(pub usize);
|
|||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct ResolvedExternalSurface {
|
||||
// YUV specific information
|
||||
pub image_dependencies: [ImageDependency; 3],
|
||||
pub yuv_planes: [YuvPlaneDescriptor; 3],
|
||||
pub yuv_color_space: YuvColorSpace,
|
||||
pub yuv_format: YuvFormat,
|
||||
pub yuv_rescale: f32,
|
||||
pub color_data: ResolvedExternalSurfaceColorData,
|
||||
pub image_buffer_kind: ImageBufferKind,
|
||||
|
||||
// Update information for a native surface if it's dirty
|
||||
pub update_params: Option<(NativeSurfaceId, DeviceIntSize)>,
|
||||
}
|
||||
|
@ -517,22 +538,40 @@ impl CompositeState {
|
|||
// For each compositor surface that was promoted, build the
|
||||
// information required for the compositor to draw it
|
||||
for external_surface in &tile_cache.external_surfaces {
|
||||
let mut yuv_planes = [
|
||||
YuvPlaneDescriptor::invalid(),
|
||||
YuvPlaneDescriptor::invalid(),
|
||||
YuvPlaneDescriptor::invalid(),
|
||||
|
||||
let mut planes = [
|
||||
ExternalPlaneDescriptor::invalid(),
|
||||
ExternalPlaneDescriptor::invalid(),
|
||||
ExternalPlaneDescriptor::invalid(),
|
||||
];
|
||||
|
||||
// Step through the image keys, and build a yuv plane descriptor for each
|
||||
let required_plane_count = external_surface.yuv_format.get_plane_num();
|
||||
// Step through the image keys, and build a plane descriptor for each
|
||||
let required_plane_count =
|
||||
match external_surface.dependency {
|
||||
ExternalSurfaceDependency::Yuv { format, .. } => {
|
||||
format.get_plane_num()
|
||||
},
|
||||
ExternalSurfaceDependency::Rgb { .. } => {
|
||||
1
|
||||
}
|
||||
};
|
||||
let mut valid_plane_count = 0;
|
||||
|
||||
let mut image_dependencies = [ImageDependency::INVALID; 3];
|
||||
|
||||
for i in 0 .. required_plane_count {
|
||||
let key = external_surface.image_dependencies[i].key;
|
||||
let plane = &mut yuv_planes[i];
|
||||
let dependency = match external_surface.dependency {
|
||||
ExternalSurfaceDependency::Yuv { image_dependencies, .. } => {
|
||||
image_dependencies[i]
|
||||
},
|
||||
ExternalSurfaceDependency::Rgb { image_dependency, .. } => {
|
||||
image_dependency
|
||||
}
|
||||
};
|
||||
image_dependencies[i] = dependency;
|
||||
|
||||
let request = ImageRequest {
|
||||
key,
|
||||
key: dependency.key,
|
||||
rendering: external_surface.image_rendering,
|
||||
tile: None,
|
||||
};
|
||||
|
@ -546,8 +585,8 @@ impl CompositeState {
|
|||
|
||||
if cache_item.texture_id != TextureSource::Invalid {
|
||||
valid_plane_count += 1;
|
||||
|
||||
*plane = YuvPlaneDescriptor {
|
||||
let plane = &mut planes[i];
|
||||
*plane = ExternalPlaneDescriptor {
|
||||
texture: cache_item.texture_id,
|
||||
texture_layer: cache_item.texture_layer,
|
||||
uv_rect: cache_item.uv_rect.into(),
|
||||
|
@ -557,7 +596,7 @@ impl CompositeState {
|
|||
|
||||
// Check if there are valid images added for each YUV plane
|
||||
if valid_plane_count < required_plane_count {
|
||||
warn!("Warnings: skip a YUV compositor surface, found {}/{} valid images",
|
||||
warn!("Warnings: skip a YUV/RGB compositor surface, found {}/{} valid images",
|
||||
valid_plane_count,
|
||||
required_plane_count,
|
||||
);
|
||||
|
@ -586,15 +625,37 @@ impl CompositeState {
|
|||
)
|
||||
});
|
||||
|
||||
self.external_surfaces.push(ResolvedExternalSurface {
|
||||
yuv_color_space: external_surface.yuv_color_space,
|
||||
yuv_format: external_surface.yuv_format,
|
||||
yuv_rescale: external_surface.yuv_rescale,
|
||||
image_buffer_kind: get_buffer_kind(yuv_planes[0].texture),
|
||||
image_dependencies: external_surface.image_dependencies,
|
||||
yuv_planes,
|
||||
update_params,
|
||||
});
|
||||
match external_surface.dependency {
|
||||
ExternalSurfaceDependency::Yuv{ color_space, format, rescale, .. } => {
|
||||
|
||||
let image_buffer_kind = get_buffer_kind(planes[0].texture);
|
||||
|
||||
self.external_surfaces.push(ResolvedExternalSurface {
|
||||
color_data: ResolvedExternalSurfaceColorData::Yuv {
|
||||
image_dependencies,
|
||||
planes,
|
||||
color_space,
|
||||
format,
|
||||
rescale,
|
||||
},
|
||||
image_buffer_kind,
|
||||
update_params,
|
||||
});
|
||||
},
|
||||
ExternalSurfaceDependency::Rgb{ .. } => {
|
||||
|
||||
let image_buffer_kind = get_buffer_kind(planes[0].texture);
|
||||
|
||||
self.external_surfaces.push(ResolvedExternalSurface {
|
||||
color_data: ResolvedExternalSurfaceColorData::Rgb {
|
||||
image_dependency: image_dependencies[0],
|
||||
plane: planes[0],
|
||||
},
|
||||
image_buffer_kind,
|
||||
update_params,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
let tile = CompositeTile {
|
||||
surface,
|
||||
|
@ -613,7 +674,7 @@ impl CompositeState {
|
|||
surface_id: external_surface.native_surface_id,
|
||||
offset: tile.rect.origin,
|
||||
clip_rect: tile.clip_rect,
|
||||
image_dependencies: external_surface.image_dependencies,
|
||||
image_dependencies: image_dependencies,
|
||||
tile_descriptors: Vec::new(),
|
||||
}
|
||||
);
|
||||
|
|
|
@ -236,6 +236,8 @@ impl ResolveInstanceData {
|
|||
}
|
||||
|
||||
/// Vertex format for picture cache composite shader.
|
||||
/// When editing the members, update desc::COMPOSITE
|
||||
/// so its list of instance_attributes matches:
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct CompositeInstance {
|
||||
|
@ -280,6 +282,27 @@ impl CompositeInstance {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_rgb(
|
||||
rect: DeviceRect,
|
||||
clip_rect: DeviceRect,
|
||||
color: PremultipliedColorF,
|
||||
layer: f32,
|
||||
z_id: ZBufferId,
|
||||
uv_rect: TexelRect,
|
||||
) -> Self {
|
||||
CompositeInstance {
|
||||
rect,
|
||||
clip_rect,
|
||||
color,
|
||||
z_id: z_id.0 as f32,
|
||||
yuv_color_space: 0.0,
|
||||
yuv_format: 0.0,
|
||||
yuv_rescale: 0.0,
|
||||
texture_layers: [layer, 0.0, 0.0],
|
||||
uv_rects: [uv_rect, uv_rect, TexelRect::invalid()],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_yuv(
|
||||
rect: DeviceRect,
|
||||
clip_rect: DeviceRect,
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind};
|
||||
use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode};
|
||||
use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags};
|
||||
use api::{ImageRendering, ColorDepth, YuvColorSpace, YuvFormat};
|
||||
use api::units::*;
|
||||
use crate::box_shadow::BLUR_SAMPLE_SCALE;
|
||||
use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId};
|
||||
|
@ -104,7 +105,7 @@ use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX,
|
|||
SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace
|
||||
};
|
||||
use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId, NativeTileId};
|
||||
use crate::composite::{ExternalSurfaceDescriptor};
|
||||
use crate::composite::{ExternalSurfaceDescriptor, ExternalSurfaceDependency};
|
||||
use crate::debug_colors;
|
||||
use euclid::{vec2, vec3, Point2D, Scale, Size2D, Vector2D, Rect, Transform3D, SideOffsets2D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
|
@ -2252,7 +2253,7 @@ pub struct NativeSurface {
|
|||
/// Hash key for an external native compositor surface
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct ExternalNativeSurfaceKey {
|
||||
/// The YUV image keys that are used to draw this surface.
|
||||
/// The YUV/RGB image keys that are used to draw this surface.
|
||||
pub image_keys: [ImageKey; 3],
|
||||
/// The current device size of the surface.
|
||||
pub size: DeviceIntSize,
|
||||
|
@ -2933,6 +2934,236 @@ impl TileCacheInstance {
|
|||
world_culling_rect
|
||||
}
|
||||
|
||||
fn can_promote_to_surface(
|
||||
&mut self,
|
||||
flags: PrimitiveFlags,
|
||||
prim_clip_chain: &ClipChainInstance,
|
||||
prim_spatial_node_index: SpatialNodeIndex,
|
||||
on_picture_surface: bool,
|
||||
frame_context: &FrameVisibilityContext,
|
||||
) -> bool {
|
||||
// Check if this primitive _wants_ to be promoted to a compositor surface.
|
||||
if !flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now, only support a small (arbitrary) number of compositor surfaces.
|
||||
if self.external_surfaces.len() == MAX_COMPOSITOR_SURFACES {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a complex clip is being applied to this primitive, it can't be
|
||||
// promoted directly to a compositor surface (we might be able to
|
||||
// do this in limited cases in future, some native compositors do
|
||||
// support rounded rect clips, for example)
|
||||
if prim_clip_chain.needs_mask {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If not on the same surface as the picture cache, it has some kind of
|
||||
// complex effect (such as a filter, mix-blend-mode or 3d transform).
|
||||
if !on_picture_surface {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the primitive is not axis-aligned with the root coordinate system,
|
||||
// it can't be promoted to a native compositor surface (could potentially
|
||||
// be supported in future on some platforms).
|
||||
let prim_spatial_node = &frame_context.spatial_tree
|
||||
.spatial_nodes[prim_spatial_node_index.0 as usize];
|
||||
if prim_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the transform has scale, we can't currently handle
|
||||
// it in the native compositor - we can support this in future though.
|
||||
if !self.map_local_to_surface.get_transform().is_simple_2d_translation() {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn setup_compositor_surfaces_yuv(
|
||||
&mut self,
|
||||
prim_info: &mut PrimitiveDependencyInfo,
|
||||
prim_rect: PictureRect,
|
||||
frame_context: &FrameVisibilityContext,
|
||||
image_dependencies: &[ImageDependency;3],
|
||||
api_keys: &[ImageKey; 3],
|
||||
resource_cache: &mut ResourceCache,
|
||||
composite_state: &mut CompositeState,
|
||||
image_rendering: ImageRendering,
|
||||
color_depth: ColorDepth,
|
||||
color_space: YuvColorSpace,
|
||||
format: YuvFormat,
|
||||
) {
|
||||
self.setup_compositor_surfaces_impl(
|
||||
prim_info,
|
||||
prim_rect,
|
||||
frame_context,
|
||||
ExternalSurfaceDependency::Yuv {
|
||||
image_dependencies: *image_dependencies,
|
||||
color_space,
|
||||
format,
|
||||
rescale: color_depth.rescaling_factor(),
|
||||
},
|
||||
api_keys,
|
||||
resource_cache,
|
||||
composite_state,
|
||||
image_rendering,
|
||||
);
|
||||
}
|
||||
|
||||
fn setup_compositor_surfaces_rgb(
|
||||
&mut self,
|
||||
prim_info: &mut PrimitiveDependencyInfo,
|
||||
prim_rect: PictureRect,
|
||||
frame_context: &FrameVisibilityContext,
|
||||
image_dependency: ImageDependency,
|
||||
api_key: ImageKey,
|
||||
resource_cache: &mut ResourceCache,
|
||||
composite_state: &mut CompositeState,
|
||||
image_rendering: ImageRendering,
|
||||
) {
|
||||
let mut api_keys = [ImageKey::DUMMY; 3];
|
||||
api_keys[0] = api_key;
|
||||
self.setup_compositor_surfaces_impl(
|
||||
prim_info,
|
||||
prim_rect,
|
||||
frame_context,
|
||||
ExternalSurfaceDependency::Rgb {
|
||||
image_dependency,
|
||||
},
|
||||
&api_keys,
|
||||
resource_cache,
|
||||
composite_state,
|
||||
image_rendering,
|
||||
);
|
||||
}
|
||||
|
||||
fn setup_compositor_surfaces_impl(
|
||||
&mut self,
|
||||
prim_info: &mut PrimitiveDependencyInfo,
|
||||
prim_rect: PictureRect,
|
||||
frame_context: &FrameVisibilityContext,
|
||||
dependency: ExternalSurfaceDependency,
|
||||
api_keys: &[ImageKey; 3],
|
||||
resource_cache: &mut ResourceCache,
|
||||
composite_state: &mut CompositeState,
|
||||
image_rendering: ImageRendering,
|
||||
) {
|
||||
prim_info.is_compositor_surface = true;
|
||||
|
||||
let pic_to_world_mapper = SpaceMapper::new_with_target(
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
self.spatial_node_index,
|
||||
frame_context.global_screen_world_rect,
|
||||
frame_context.spatial_tree,
|
||||
);
|
||||
|
||||
let world_rect = pic_to_world_mapper
|
||||
.map(&prim_rect)
|
||||
.expect("bug: unable to map the primitive to world space");
|
||||
let world_clip_rect = pic_to_world_mapper
|
||||
.map(&prim_info.prim_clip_rect)
|
||||
.expect("bug: unable to map clip to world space");
|
||||
|
||||
let is_visible = world_clip_rect.intersects(&frame_context.global_screen_world_rect);
|
||||
if !is_visible {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(gw): Is there any case where if the primitive ends up on a fractional
|
||||
// boundary we want to _skip_ promoting to a compositor surface and
|
||||
// draw it as part of the content?
|
||||
let device_rect = (world_rect * frame_context.global_device_pixel_scale).round();
|
||||
let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round();
|
||||
|
||||
// When using native compositing, we need to find an existing native surface
|
||||
// handle to use, or allocate a new one. For existing native surfaces, we can
|
||||
// also determine whether this needs to be updated, depending on whether the
|
||||
// image generation(s) of the planes have changed since last composite.
|
||||
let (native_surface_id, update_params) = match composite_state.compositor_kind {
|
||||
CompositorKind::Draw { .. } => {
|
||||
(None, None)
|
||||
}
|
||||
CompositorKind::Native { .. } => {
|
||||
let native_surface_size = device_rect.size.round().to_i32();
|
||||
|
||||
let key = ExternalNativeSurfaceKey {
|
||||
image_keys: *api_keys,
|
||||
size: native_surface_size,
|
||||
};
|
||||
|
||||
let native_surface = self.external_native_surface_cache
|
||||
.entry(key)
|
||||
.or_insert_with(|| {
|
||||
// No existing surface, so allocate a new compositor surface and
|
||||
// a single compositor tile that covers the entire compositor surface.
|
||||
|
||||
let native_surface_id = resource_cache.create_compositor_surface(
|
||||
DeviceIntPoint::zero(),
|
||||
native_surface_size,
|
||||
true,
|
||||
);
|
||||
|
||||
let tile_id = NativeTileId {
|
||||
surface_id: native_surface_id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
resource_cache.create_compositor_tile(tile_id);
|
||||
|
||||
ExternalNativeSurface {
|
||||
used_this_frame: true,
|
||||
native_surface_id,
|
||||
image_dependencies: [ImageDependency::INVALID; 3],
|
||||
}
|
||||
});
|
||||
|
||||
// Mark that the surface is referenced this frame so that the
|
||||
// backing native surface handle isn't freed.
|
||||
native_surface.used_this_frame = true;
|
||||
|
||||
// If the image dependencies match, there is no need to update
|
||||
// the backing native surface.
|
||||
let update_params = match dependency {
|
||||
ExternalSurfaceDependency::Yuv{ image_dependencies, .. } => {
|
||||
if image_dependencies == native_surface.image_dependencies {
|
||||
None
|
||||
} else {
|
||||
Some(native_surface_size)
|
||||
}
|
||||
},
|
||||
ExternalSurfaceDependency::Rgb{ image_dependency, .. } => {
|
||||
if image_dependency == native_surface.image_dependencies[0] {
|
||||
None
|
||||
} else {
|
||||
Some(native_surface_size)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
(Some(native_surface.native_surface_id), update_params)
|
||||
}
|
||||
};
|
||||
|
||||
// Each compositor surface allocates a unique z-id
|
||||
self.external_surfaces.push(ExternalSurfaceDescriptor {
|
||||
local_rect: prim_info.prim_clip_rect,
|
||||
world_rect,
|
||||
local_clip_rect: prim_info.prim_clip_rect,
|
||||
dependency,
|
||||
image_rendering,
|
||||
device_rect,
|
||||
clip_rect,
|
||||
z_id: composite_state.z_generator.next(),
|
||||
native_surface_id,
|
||||
update_params,
|
||||
});
|
||||
}
|
||||
|
||||
/// Update the dependencies for each tile for a given primitive instance.
|
||||
pub fn update_prim_dependencies(
|
||||
&mut self,
|
||||
|
@ -3064,6 +3295,7 @@ impl TileCacheInstance {
|
|||
// then applied below.
|
||||
let mut backdrop_candidate = None;
|
||||
|
||||
|
||||
// For pictures, we don't (yet) know the valid clip rect, so we can't correctly
|
||||
// use it to calculate the local bounding rect for the tiles. If we include them
|
||||
// then we may calculate a bounding rect that is too large, since it won't include
|
||||
|
@ -3113,11 +3345,24 @@ impl TileCacheInstance {
|
|||
|
||||
prim_info.clip_by_tile = true;
|
||||
}
|
||||
PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => {
|
||||
let image_data = &data_stores.image[data_handle].kind;
|
||||
PrimitiveInstanceKind::Image { data_handle, image_instance_index, ref mut is_compositor_surface, .. } => {
|
||||
let image_key = &data_stores.image[data_handle];
|
||||
let image_data = &image_key.kind;
|
||||
let image_instance = &image_instances[image_instance_index];
|
||||
let opacity_binding_index = image_instance.opacity_binding_index;
|
||||
|
||||
let mut promote_to_surface = false;
|
||||
// If picture caching is disabled, we can't support any compositor surfaces.
|
||||
if composite_state.picture_caching_is_enabled &&
|
||||
self.can_promote_to_surface(image_key.common.flags,
|
||||
prim_clip_chain,
|
||||
prim_spatial_node_index,
|
||||
on_picture_surface,
|
||||
frame_context) {
|
||||
promote_to_surface = true;
|
||||
}
|
||||
*is_compositor_surface = promote_to_surface;
|
||||
|
||||
if opacity_binding_index == OpacityBindingIndex::INVALID {
|
||||
if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) {
|
||||
// For an image to be a possible opaque backdrop, it must:
|
||||
|
@ -3140,10 +3385,26 @@ impl TileCacheInstance {
|
|||
}
|
||||
}
|
||||
|
||||
prim_info.images.push(ImageDependency {
|
||||
key: image_data.key,
|
||||
generation: resource_cache.get_image_generation(image_data.key),
|
||||
});
|
||||
if promote_to_surface {
|
||||
self.setup_compositor_surfaces_rgb(
|
||||
&mut prim_info,
|
||||
prim_rect,
|
||||
frame_context,
|
||||
ImageDependency {
|
||||
key: image_data.key,
|
||||
generation: resource_cache.get_image_generation(image_data.key),
|
||||
},
|
||||
image_data.key,
|
||||
resource_cache,
|
||||
composite_state,
|
||||
image_data.image_rendering,
|
||||
);
|
||||
} else {
|
||||
prim_info.images.push(ImageDependency {
|
||||
key: image_data.key,
|
||||
generation: resource_cache.get_image_generation(image_data.key),
|
||||
});
|
||||
}
|
||||
}
|
||||
PrimitiveInstanceKind::YuvImage { data_handle, ref mut is_compositor_surface, .. } => {
|
||||
let prim_data = &data_stores.yuv_image[data_handle];
|
||||
|
@ -3152,50 +3413,17 @@ impl TileCacheInstance {
|
|||
// extract the logic below and support RGBA compositor surfaces too.
|
||||
let mut promote_to_surface = false;
|
||||
|
||||
|
||||
// If picture caching is disabled, we can't support any compositor surfaces.
|
||||
if composite_state.picture_caching_is_enabled {
|
||||
// Check if this primitive _wants_ to be promoted to a compositor surface.
|
||||
if prim_data.common.flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) {
|
||||
promote_to_surface = true;
|
||||
promote_to_surface = self.can_promote_to_surface(
|
||||
prim_data.common.flags,
|
||||
prim_clip_chain,
|
||||
prim_spatial_node_index,
|
||||
on_picture_surface,
|
||||
frame_context);
|
||||
|
||||
// For now, only support a small (arbitrary) number of compositor surfaces.
|
||||
if self.external_surfaces.len() == MAX_COMPOSITOR_SURFACES {
|
||||
promote_to_surface = false;
|
||||
}
|
||||
|
||||
// If a complex clip is being applied to this primitive, it can't be
|
||||
// promoted directly to a compositor surface (we might be able to
|
||||
// do this in limited cases in future, some native compositors do
|
||||
// support rounded rect clips, for example)
|
||||
if prim_clip_chain.needs_mask {
|
||||
promote_to_surface = false;
|
||||
}
|
||||
|
||||
// If not on the same surface as the picture cache, it has some kind of
|
||||
// complex effect (such as a filter, mix-blend-mode or 3d transform).
|
||||
if !on_picture_surface {
|
||||
promote_to_surface = false;
|
||||
}
|
||||
|
||||
// If the primitive is not axis-aligned with the root coordinate system,
|
||||
// it can't be promoted to a native compositor surface (could potentially
|
||||
// be supported in future on some platforms).
|
||||
let prim_spatial_node = &frame_context.spatial_tree
|
||||
.spatial_nodes[prim_spatial_node_index.0 as usize];
|
||||
if prim_spatial_node.coordinate_system_id != CoordinateSystemId::root() {
|
||||
promote_to_surface = false;
|
||||
}
|
||||
|
||||
// If the transform has scale, we can't currently handle
|
||||
// it in the native compositor - we can support this in future though.
|
||||
if !self.map_local_to_surface.get_transform().is_simple_2d_translation() {
|
||||
promote_to_surface = false;
|
||||
}
|
||||
|
||||
// TODO(gw): When we support RGBA images for external surfaces, we also
|
||||
// need to check if opaque (YUV images are implicitly opaque).
|
||||
}
|
||||
// TODO(gw): When we support RGBA images for external surfaces, we also
|
||||
// need to check if opaque (YUV images are implicitly opaque).
|
||||
}
|
||||
|
||||
// Store on the YUV primitive instance whether this is a promoted surface.
|
||||
|
@ -3209,116 +3437,29 @@ impl TileCacheInstance {
|
|||
// a promoted surface, since we don't want the tiles to invalidate when the
|
||||
// video content changes, if it's a compositor surface!
|
||||
if promote_to_surface {
|
||||
prim_info.is_compositor_surface = true;
|
||||
|
||||
let pic_to_world_mapper = SpaceMapper::new_with_target(
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
self.spatial_node_index,
|
||||
frame_context.global_screen_world_rect,
|
||||
frame_context.spatial_tree,
|
||||
);
|
||||
|
||||
let world_rect = pic_to_world_mapper
|
||||
.map(&prim_rect)
|
||||
.expect("bug: unable to map the primitive to world space");
|
||||
let world_clip_rect = pic_to_world_mapper
|
||||
.map(&prim_info.prim_clip_rect)
|
||||
.expect("bug: unable to map clip to world space");
|
||||
|
||||
let is_visible = world_clip_rect.intersects(&frame_context.global_screen_world_rect);
|
||||
if is_visible {
|
||||
// TODO(gw): Is there any case where if the primitive ends up on a fractional
|
||||
// boundary we want to _skip_ promoting to a compositor surface and
|
||||
// draw it as part of the content?
|
||||
let device_rect = (world_rect * frame_context.global_device_pixel_scale).round();
|
||||
let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round();
|
||||
|
||||
// Build dependency for each YUV plane, with current image generation for
|
||||
// later detection of when the composited surface has changed.
|
||||
let mut image_dependencies = [ImageDependency::INVALID; 3];
|
||||
for (key, dep) in prim_data.kind.yuv_key.iter().cloned().zip(image_dependencies.iter_mut()) {
|
||||
*dep = ImageDependency {
|
||||
key,
|
||||
generation: resource_cache.get_image_generation(key),
|
||||
}
|
||||
// Build dependency for each YUV plane, with current image generation for
|
||||
// later detection of when the composited surface has changed.
|
||||
let mut image_dependencies = [ImageDependency::INVALID; 3];
|
||||
for (key, dep) in prim_data.kind.yuv_key.iter().cloned().zip(image_dependencies.iter_mut()) {
|
||||
*dep = ImageDependency {
|
||||
key,
|
||||
generation: resource_cache.get_image_generation(key),
|
||||
}
|
||||
|
||||
// When using native compositing, we need to find an existing native surface
|
||||
// handle to use, or allocate a new one. For existing native surfaces, we can
|
||||
// also determine whether this needs to be updated, depending on whether the
|
||||
// image generation(s) of the YUV planes have changed since last composite.
|
||||
let (native_surface_id, update_params) = match composite_state.compositor_kind {
|
||||
CompositorKind::Draw { .. } => {
|
||||
(None, None)
|
||||
}
|
||||
CompositorKind::Native { .. } => {
|
||||
let native_surface_size = device_rect.size.round().to_i32();
|
||||
|
||||
let key = ExternalNativeSurfaceKey {
|
||||
image_keys: prim_data.kind.yuv_key,
|
||||
size: native_surface_size,
|
||||
};
|
||||
|
||||
let native_surface = self.external_native_surface_cache
|
||||
.entry(key)
|
||||
.or_insert_with(|| {
|
||||
// No existing surface, so allocate a new compositor surface and
|
||||
// a single compositor tile that covers the entire compositor surface.
|
||||
|
||||
let native_surface_id = resource_cache.create_compositor_surface(
|
||||
DeviceIntPoint::zero(),
|
||||
native_surface_size,
|
||||
true,
|
||||
);
|
||||
|
||||
let tile_id = NativeTileId {
|
||||
surface_id: native_surface_id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
resource_cache.create_compositor_tile(tile_id);
|
||||
|
||||
ExternalNativeSurface {
|
||||
used_this_frame: true,
|
||||
native_surface_id,
|
||||
image_dependencies: [ImageDependency::INVALID; 3],
|
||||
}
|
||||
});
|
||||
|
||||
// Mark that the surface is referenced this frame so that the
|
||||
// backing native surface handle isn't freed.
|
||||
native_surface.used_this_frame = true;
|
||||
|
||||
// If the image dependencies match, there is no need to update
|
||||
// the backing native surface.
|
||||
let update_params = if image_dependencies == native_surface.image_dependencies {
|
||||
None
|
||||
} else {
|
||||
Some(native_surface_size)
|
||||
};
|
||||
|
||||
(Some(native_surface.native_surface_id), update_params)
|
||||
}
|
||||
};
|
||||
|
||||
// Each compositor surface allocates a unique z-id
|
||||
self.external_surfaces.push(ExternalSurfaceDescriptor {
|
||||
local_rect: prim_info.prim_clip_rect,
|
||||
world_rect,
|
||||
local_clip_rect: prim_info.prim_clip_rect,
|
||||
image_dependencies,
|
||||
image_rendering: prim_data.kind.image_rendering,
|
||||
device_rect,
|
||||
clip_rect,
|
||||
yuv_color_space: prim_data.kind.color_space,
|
||||
yuv_format: prim_data.kind.format,
|
||||
yuv_rescale: prim_data.kind.color_depth.rescaling_factor(),
|
||||
z_id: composite_state.z_generator.next(),
|
||||
native_surface_id,
|
||||
update_params,
|
||||
});
|
||||
}
|
||||
|
||||
self.setup_compositor_surfaces_yuv(
|
||||
&mut prim_info,
|
||||
prim_rect,
|
||||
frame_context,
|
||||
&image_dependencies,
|
||||
&prim_data.kind.yuv_key,
|
||||
resource_cache,
|
||||
composite_state,
|
||||
prim_data.kind.image_rendering,
|
||||
prim_data.kind.color_depth,
|
||||
prim_data.kind.color_space,
|
||||
prim_data.kind.format,
|
||||
);
|
||||
} else {
|
||||
prim_info.images.extend(
|
||||
prim_data.kind.yuv_key.iter().map(|key| {
|
||||
|
|
|
@ -332,6 +332,7 @@ impl InternablePrimitive for Image {
|
|||
PrimitiveInstanceKind::Image {
|
||||
data_handle,
|
||||
image_instance_index,
|
||||
is_compositor_surface: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1428,6 +1428,7 @@ pub enum PrimitiveInstanceKind {
|
|||
/// Handle to the common interned data for this primitive.
|
||||
data_handle: ImageDataHandle,
|
||||
image_instance_index: ImageInstanceIndex,
|
||||
is_compositor_surface: bool,
|
||||
},
|
||||
LinearGradient {
|
||||
/// Handle to the common interned data for this primitive.
|
||||
|
|
|
@ -48,7 +48,7 @@ use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures,
|
|||
#[cfg(any(feature = "capture", feature = "replay"))]
|
||||
use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
|
||||
use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, ResolvedExternalSurface};
|
||||
use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeSurfaceFormat};
|
||||
use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData};
|
||||
use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation};
|
||||
use crate::debug_colors;
|
||||
use crate::debug_render::{DebugItem, DebugRenderer};
|
||||
|
@ -4530,54 +4530,90 @@ impl Renderer {
|
|||
self.device.ortho_far_plane(),
|
||||
);
|
||||
|
||||
// Bind an appropriate YUV shader for the texture format kind
|
||||
self.shaders
|
||||
.borrow_mut()
|
||||
.get_composite_shader(
|
||||
CompositeSurfaceFormat::Yuv,
|
||||
surface.image_buffer_kind,
|
||||
).bind(
|
||||
&mut self.device,
|
||||
&projection,
|
||||
&mut self.renderer_errors
|
||||
);
|
||||
let ( textures, instance ) = match surface.color_data {
|
||||
ResolvedExternalSurfaceColorData::Yuv{
|
||||
ref planes, color_space, format, rescale, .. } => {
|
||||
|
||||
let textures = BatchTextures {
|
||||
colors: [
|
||||
surface.yuv_planes[0].texture,
|
||||
surface.yuv_planes[1].texture,
|
||||
surface.yuv_planes[2].texture,
|
||||
],
|
||||
// Bind an appropriate YUV shader for the texture format kind
|
||||
self.shaders
|
||||
.borrow_mut()
|
||||
.get_composite_shader(
|
||||
CompositeSurfaceFormat::Yuv,
|
||||
surface.image_buffer_kind,
|
||||
).bind(
|
||||
&mut self.device,
|
||||
&projection,
|
||||
&mut self.renderer_errors
|
||||
);
|
||||
|
||||
let textures = BatchTextures {
|
||||
colors: [
|
||||
planes[0].texture,
|
||||
planes[1].texture,
|
||||
planes[2].texture,
|
||||
],
|
||||
};
|
||||
|
||||
// When the texture is an external texture, the UV rect is not known when
|
||||
// the external surface descriptor is created, because external textures
|
||||
// are not resolved until the lock() callback is invoked at the start of
|
||||
// the frame render. To handle this, query the texture resolver for the
|
||||
// UV rect if it's an external texture, otherwise use the default UV rect.
|
||||
let uv_rects = [
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[0], planes[0].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[1], planes[1].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[2], planes[2].uv_rect),
|
||||
];
|
||||
|
||||
let instance = CompositeInstance::new_yuv(
|
||||
surface_rect.to_f32(),
|
||||
surface_rect.to_f32(),
|
||||
// z-id is not relevant when updating a native compositor surface.
|
||||
// TODO(gw): Support compositor surfaces without z-buffer, for memory / perf win here.
|
||||
ZBufferId(0),
|
||||
color_space,
|
||||
format,
|
||||
rescale,
|
||||
[
|
||||
planes[0].texture_layer as f32,
|
||||
planes[1].texture_layer as f32,
|
||||
planes[2].texture_layer as f32,
|
||||
],
|
||||
uv_rects,
|
||||
);
|
||||
|
||||
( textures, instance )
|
||||
},
|
||||
ResolvedExternalSurfaceColorData::Rgb{ ref plane, .. } => {
|
||||
|
||||
self.shaders
|
||||
.borrow_mut()
|
||||
.get_composite_shader(
|
||||
CompositeSurfaceFormat::Rgba,
|
||||
surface.image_buffer_kind,
|
||||
).bind(
|
||||
&mut self.device,
|
||||
&projection,
|
||||
&mut self.renderer_errors
|
||||
);
|
||||
|
||||
let textures = BatchTextures::color(plane.texture);
|
||||
|
||||
let uv_rect = self.texture_resolver.get_uv_rect(&textures.colors[0], plane.uv_rect);
|
||||
|
||||
let instance = CompositeInstance::new_rgb(
|
||||
surface_rect.to_f32(),
|
||||
surface_rect.to_f32(),
|
||||
PremultipliedColorF::WHITE,
|
||||
plane.texture_layer as f32,
|
||||
ZBufferId(0),
|
||||
uv_rect,
|
||||
);
|
||||
|
||||
( textures, instance )
|
||||
},
|
||||
};
|
||||
|
||||
// When the texture is an external texture, the UV rect is not known when
|
||||
// the external surface descriptor is created, because external textures
|
||||
// are not resolved until the lock() callback is invoked at the start of
|
||||
// the frame render. To handle this, query the texture resolver for the
|
||||
// UV rect if it's an external texture, otherwise use the default UV rect.
|
||||
let uv_rects = [
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect),
|
||||
];
|
||||
|
||||
let instance = CompositeInstance::new_yuv(
|
||||
surface_rect.to_f32(),
|
||||
surface_rect.to_f32(),
|
||||
// z-id is not relevant when updating a native compositor surface.
|
||||
// TODO(gw): Support compositor surfaces without z-buffer, for memory / perf win here.
|
||||
ZBufferId(0),
|
||||
surface.yuv_color_space,
|
||||
surface.yuv_format,
|
||||
surface.yuv_rescale,
|
||||
[
|
||||
surface.yuv_planes[0].texture_layer as f32,
|
||||
surface.yuv_planes[1].texture_layer as f32,
|
||||
surface.yuv_planes[2].texture_layer as f32,
|
||||
],
|
||||
uv_rects,
|
||||
);
|
||||
|
||||
self.draw_instanced_batch(
|
||||
&[instance],
|
||||
VertexArrayKind::Composite,
|
||||
|
@ -4686,43 +4722,64 @@ impl Renderer {
|
|||
CompositeTileSurface::ExternalSurface { external_surface_index } => {
|
||||
let surface = &external_surfaces[external_surface_index.0];
|
||||
|
||||
let textures = BatchTextures {
|
||||
colors: [
|
||||
surface.yuv_planes[0].texture,
|
||||
surface.yuv_planes[1].texture,
|
||||
surface.yuv_planes[2].texture,
|
||||
],
|
||||
};
|
||||
match surface.color_data {
|
||||
ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, rescale, .. } => {
|
||||
|
||||
// When the texture is an external texture, the UV rect is not known when
|
||||
// the external surface descriptor is created, because external textures
|
||||
// are not resolved until the lock() callback is invoked at the start of
|
||||
// the frame render. To handle this, query the texture resolver for the
|
||||
// UV rect if it's an external texture, otherwise use the default UV rect.
|
||||
let uv_rects = [
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[0], surface.yuv_planes[0].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[1], surface.yuv_planes[1].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[2], surface.yuv_planes[2].uv_rect),
|
||||
];
|
||||
let textures = BatchTextures {
|
||||
colors: [
|
||||
planes[0].texture,
|
||||
planes[1].texture,
|
||||
planes[2].texture,
|
||||
],
|
||||
};
|
||||
|
||||
(
|
||||
CompositeInstance::new_yuv(
|
||||
tile.rect,
|
||||
clip_rect,
|
||||
tile.z_id,
|
||||
surface.yuv_color_space,
|
||||
surface.yuv_format,
|
||||
surface.yuv_rescale,
|
||||
[
|
||||
surface.yuv_planes[0].texture_layer as f32,
|
||||
surface.yuv_planes[1].texture_layer as f32,
|
||||
surface.yuv_planes[2].texture_layer as f32,
|
||||
],
|
||||
uv_rects,
|
||||
),
|
||||
textures,
|
||||
(CompositeSurfaceFormat::Yuv, surface.image_buffer_kind),
|
||||
)
|
||||
// When the texture is an external texture, the UV rect is not known when
|
||||
// the external surface descriptor is created, because external textures
|
||||
// are not resolved until the lock() callback is invoked at the start of
|
||||
// the frame render. To handle this, query the texture resolver for the
|
||||
// UV rect if it's an external texture, otherwise use the default UV rect.
|
||||
let uv_rects = [
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[0], planes[0].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[1], planes[1].uv_rect),
|
||||
self.texture_resolver.get_uv_rect(&textures.colors[2], planes[2].uv_rect),
|
||||
];
|
||||
|
||||
(
|
||||
CompositeInstance::new_yuv(
|
||||
tile.rect,
|
||||
clip_rect,
|
||||
tile.z_id,
|
||||
color_space,
|
||||
format,
|
||||
rescale,
|
||||
[
|
||||
planes[0].texture_layer as f32,
|
||||
planes[1].texture_layer as f32,
|
||||
planes[2].texture_layer as f32,
|
||||
],
|
||||
uv_rects,
|
||||
),
|
||||
textures,
|
||||
(CompositeSurfaceFormat::Yuv, surface.image_buffer_kind),
|
||||
)
|
||||
},
|
||||
ResolvedExternalSurfaceColorData::Rgb{ ref plane, .. } => {
|
||||
let uv_rect = self.texture_resolver.get_uv_rect(&plane.texture, plane.uv_rect);
|
||||
|
||||
(
|
||||
CompositeInstance::new_rgb(
|
||||
tile.rect,
|
||||
clip_rect,
|
||||
PremultipliedColorF::WHITE,
|
||||
plane.texture_layer as f32,
|
||||
tile.z_id,
|
||||
uv_rect,
|
||||
),
|
||||
BatchTextures::color(plane.texture),
|
||||
(CompositeSurfaceFormat::Rgba, surface.image_buffer_kind),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => {
|
||||
unreachable!("bug: found native surface in simple composite path");
|
||||
|
|
|
@ -579,7 +579,11 @@ pub struct Shaders {
|
|||
// cache tiles at a lower level (e.g. in DWM for Windows); in that case we
|
||||
// directly hand the picture cache surfaces over to the OS Compositor, and
|
||||
// our own Composite shaders below never run.
|
||||
pub composite_rgba: LazilyCompiledShader,
|
||||
// To composite external (RGB) surfaces we need various permutations of
|
||||
// shaders with WR_FEATURE flags on or off based on the type of image
|
||||
// buffer we're sourcing from (see IMAGE_BUFFER_KINDS).
|
||||
pub composite_rgba: Vec<Option<LazilyCompiledShader>>,
|
||||
// The same set of composite shaders but with WR_FEATURE_YUV added.
|
||||
pub composite_yuv: Vec<Option<LazilyCompiledShader>>,
|
||||
}
|
||||
|
||||
|
@ -884,13 +888,16 @@ impl Shaders {
|
|||
|
||||
// All yuv_image configuration.
|
||||
let mut yuv_features = Vec::new();
|
||||
let mut rgba_features = Vec::new();
|
||||
let yuv_shader_num = IMAGE_BUFFER_KINDS.len();
|
||||
let mut brush_yuv_image = Vec::new();
|
||||
let mut composite_yuv = Vec::new();
|
||||
let mut composite_rgba = Vec::new();
|
||||
// PrimitiveShader is not clonable. Use push() to initialize the vec.
|
||||
for _ in 0 .. yuv_shader_num {
|
||||
brush_yuv_image.push(None);
|
||||
composite_yuv.push(None);
|
||||
composite_rgba.push(None);
|
||||
}
|
||||
for image_buffer_kind in &IMAGE_BUFFER_KINDS {
|
||||
if image_buffer_kind.has_platform_support(&gl_type) {
|
||||
|
@ -899,6 +906,7 @@ impl Shaders {
|
|||
let feature_string = image_buffer_kind.get_feature_string();
|
||||
if feature_string != "" {
|
||||
yuv_features.push(feature_string);
|
||||
rgba_features.push(feature_string);
|
||||
}
|
||||
|
||||
let brush_shader = BrushShader::new(
|
||||
|
@ -912,7 +920,7 @@ impl Shaders {
|
|||
use_pixel_local_storage,
|
||||
)?;
|
||||
|
||||
let composite_shader = LazilyCompiledShader::new(
|
||||
let composite_yuv_shader = LazilyCompiledShader::new(
|
||||
ShaderKind::Composite,
|
||||
"composite",
|
||||
&yuv_features,
|
||||
|
@ -921,13 +929,24 @@ impl Shaders {
|
|||
&shader_list,
|
||||
)?;
|
||||
|
||||
let index = Self::get_yuv_shader_index(
|
||||
let composite_rgba_shader = LazilyCompiledShader::new(
|
||||
ShaderKind::Composite,
|
||||
"composite",
|
||||
&rgba_features,
|
||||
device,
|
||||
options.precache_flags,
|
||||
&shader_list,
|
||||
)?;
|
||||
|
||||
let index = Self::get_compositing_shader_index(
|
||||
*image_buffer_kind,
|
||||
);
|
||||
brush_yuv_image[index] = Some(brush_shader);
|
||||
composite_yuv[index] = Some(composite_shader);
|
||||
composite_yuv[index] = Some(composite_yuv_shader);
|
||||
composite_rgba[index] = Some(composite_rgba_shader);
|
||||
|
||||
yuv_features.clear();
|
||||
rgba_features.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -967,15 +986,6 @@ impl Shaders {
|
|||
&shader_list,
|
||||
)?;
|
||||
|
||||
let composite_rgba = LazilyCompiledShader::new(
|
||||
ShaderKind::Composite,
|
||||
"composite",
|
||||
&[],
|
||||
device,
|
||||
options.precache_flags,
|
||||
&shader_list,
|
||||
)?;
|
||||
|
||||
Ok(Shaders {
|
||||
cs_blur_a8,
|
||||
cs_blur_rgba8,
|
||||
|
@ -1009,7 +1019,7 @@ impl Shaders {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_yuv_shader_index(buffer_kind: ImageBufferKind) -> usize {
|
||||
fn get_compositing_shader_index(buffer_kind: ImageBufferKind) -> usize {
|
||||
buffer_kind as usize
|
||||
}
|
||||
|
||||
|
@ -1020,11 +1030,13 @@ impl Shaders {
|
|||
) -> &mut LazilyCompiledShader {
|
||||
match format {
|
||||
CompositeSurfaceFormat::Rgba => {
|
||||
debug_assert_eq!(buffer_kind, ImageBufferKind::Texture2DArray);
|
||||
&mut self.composite_rgba
|
||||
let shader_index = Self::get_compositing_shader_index(buffer_kind);
|
||||
self.composite_rgba[shader_index]
|
||||
.as_mut()
|
||||
.expect("bug: unsupported rgba shader requested")
|
||||
}
|
||||
CompositeSurfaceFormat::Yuv => {
|
||||
let shader_index = Self::get_yuv_shader_index(buffer_kind);
|
||||
let shader_index = Self::get_compositing_shader_index(buffer_kind);
|
||||
self.composite_yuv[shader_index]
|
||||
.as_mut()
|
||||
.expect("bug: unsupported yuv shader requested")
|
||||
|
@ -1072,7 +1084,7 @@ impl Shaders {
|
|||
}
|
||||
BrushBatchKind::YuvImage(image_buffer_kind, ..) => {
|
||||
let shader_index =
|
||||
Self::get_yuv_shader_index(image_buffer_kind);
|
||||
Self::get_compositing_shader_index(image_buffer_kind);
|
||||
self.brush_yuv_image[shader_index]
|
||||
.as_mut()
|
||||
.expect("Unsupported YUV shader kind")
|
||||
|
@ -1139,7 +1151,11 @@ impl Shaders {
|
|||
self.cs_line_decoration.deinit(device);
|
||||
self.cs_border_segment.deinit(device);
|
||||
self.ps_split_composite.deinit(device);
|
||||
self.composite_rgba.deinit(device);
|
||||
for shader in self.composite_rgba {
|
||||
if let Some(shader) = shader {
|
||||
shader.deinit(device);
|
||||
}
|
||||
}
|
||||
for shader in self.composite_yuv {
|
||||
if let Some(shader) = shader {
|
||||
shader.deinit(device);
|
||||
|
|
|
@ -113,8 +113,12 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures {
|
|||
}
|
||||
shaders.insert("brush_image", image_features);
|
||||
|
||||
let mut composite_features: Vec<String> = Vec::new();
|
||||
for texture_type in &texture_types {
|
||||
let base = concat_features("", texture_type);
|
||||
composite_features.push(base.clone());
|
||||
}
|
||||
// YUV image brush shaders
|
||||
let mut composite_features: Vec<String> = vec!["".to_string()];
|
||||
let mut yuv_features: Vec<String> = Vec::new();
|
||||
for texture_type in &texture_types {
|
||||
let base = concat_features("YUV", texture_type);
|
||||
|
|
Загрузка…
Ссылка в новой задаче