Bug 1589512 - Part 1 - Add OS compositor interface to webrender. r=kvark,mstange

Differential Revision: https://phabricator.services.mozilla.com/D49868

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Glenn Watson 2019-10-23 20:15:58 +00:00
Родитель f351c92962
Коммит dd724015d2
10 изменённых файлов: 531 добавлений и 81 удалений

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

@ -1224,13 +1224,12 @@ impl BatchBuilder {
z_id,
});
}
TileSurface::Texture { handle, .. } => {
let cache_item = ctx.resource_cache.texture_cache.get(handle);
TileSurface::Texture { descriptor, .. } => {
let surface = descriptor.resolve(ctx.resource_cache);
let composite_tile = CompositeTile {
surface: CompositeTileSurface::Texture {
texture_id: cache_item.texture_id,
texture_layer: cache_item.texture_layer,
surface,
},
rect: device_rect,
dirty_rect,

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

@ -3,22 +3,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::ColorF;
use api::units::DeviceRect;
use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint};
use crate::gpu_types::{ZBufferId, ZBufferIdGenerator};
use crate::internal_types::TextureSource;
use crate::picture::{ResolvedSurfaceTexture, SurfaceTextureDescriptor};
/*
Types and definitions related to compositing picture cache tiles
and/or OS compositor integration.
*/
/// Describes the source surface information for a tile to be composited
/// Describes details of an operation to apply to a native surface
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum NativeSurfaceOperationDetails {
CreateSurface {
size: DeviceIntSize,
},
DestroySurface,
}
/// Describes an operation to apply to a native surface
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct NativeSurfaceOperation {
pub id: NativeSurfaceId,
pub details: NativeSurfaceOperationDetails,
}
/// Describes the source surface information for a tile to be composited. This
/// is the analog of the TileSurface type, with target surface information
/// resolved such that it can be used by the renderer.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum CompositeTileSurface {
Texture {
texture_id: TextureSource,
texture_layer: i32,
surface: ResolvedSurfaceTexture,
},
Color {
color: ColorF,
@ -37,6 +58,19 @@ pub struct CompositeTile {
pub z_id: ZBufferId,
}
/// Defines what code path WR is using to composite picture cache tiles.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CompositeMode {
/// Let WR draw tiles via normal batching. This requires no special OS support.
Draw,
/// Use a native OS compositor to draw tiles. This requires clients to implement
/// the Compositor trait, but can be significantly more power efficient on operating
/// systems that support it.
Native,
}
/// The list of tiles to be drawn this frame
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@ -52,16 +86,130 @@ pub struct CompositeState {
// it gives us the ability to partial present for any non-scroll
// case as a simple win (e.g. video, animation etc).
pub dirty_rects_are_valid: bool,
/// List of OS native surface create / destroy operations to
/// apply when render occurs.
pub native_surface_updates: Vec<NativeSurfaceOperation>,
/// The current mode for compositing picture cache tiles (e.g. drawn by WR, or OS compositor)
pub composite_mode: CompositeMode,
}
impl CompositeState {
pub fn new() -> Self {
/// Construct a new state for compositing picture tiles. This is created
/// during each frame construction and passed to the renderer.
pub fn new(composite_mode: CompositeMode) -> Self {
CompositeState {
opaque_tiles: Vec::new(),
alpha_tiles: Vec::new(),
clear_tiles: Vec::new(),
z_generator: ZBufferIdGenerator::new(0),
dirty_rects_are_valid: true,
native_surface_updates: Vec::new(),
composite_mode,
}
}
/// Queue up allocation of a new OS native compositor surface with the
/// specified id and dimensions.
pub fn create_surface(
&mut self,
id: NativeSurfaceId,
size: DeviceIntSize,
) -> SurfaceTextureDescriptor {
debug_assert_eq!(self.composite_mode, CompositeMode::Native);
self.native_surface_updates.push(
NativeSurfaceOperation {
id,
details: NativeSurfaceOperationDetails::CreateSurface {
size,
}
}
);
SurfaceTextureDescriptor::NativeSurface {
id,
size,
}
}
/// Queue up destruction of an existing native OS surface. This is used when
/// a picture cache tile is dropped or resized.
pub fn destroy_surface(
&mut self,
id: NativeSurfaceId,
) {
debug_assert_eq!(self.composite_mode, CompositeMode::Native);
self.native_surface_updates.push(
NativeSurfaceOperation {
id,
details: NativeSurfaceOperationDetails::DestroySurface,
}
);
}
}
/// An arbitrary identifier for a native (OS compositor) surface
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct NativeSurfaceId(pub u64);
/// Defines an interface to a native (OS level) compositor. If supplied
/// by the client application, then picture cache slices will be
/// composited by the OS compositor, rather than drawn via WR batches.
pub trait Compositor {
/// Create a new OS compositor surface with the given properties.
/// The surface is allocated but not placed in the visual tree.
fn create_surface(
&mut self,
id: NativeSurfaceId,
size: DeviceIntSize,
);
/// Destroy the surface with the specified id.
fn destroy_surface(
&mut self,
id: NativeSurfaceId,
);
/// Bind this surface such that WR can issue OpenGL commands
/// that will target the surface. Returns an (x, y) offset
/// where WR should draw into the surface. This can be set
/// to (0, 0) if the OS doesn't use texture atlases.
fn bind(
&mut self,
id: NativeSurfaceId,
) -> DeviceIntPoint;
/// Unbind the surface. This is called by WR when it has
/// finished issuing OpenGL commands on the current surface.
fn unbind(
&mut self,
);
/// Begin the frame
fn begin_frame(&mut self);
/// Add a surface to the visual tree to be composited. Visuals must
/// be added every frame, between the begin/end transaction call. The
/// z-order of the surfaces is determined by the order they are added
/// to the visual tree.
// TODO(gw): Adding visuals every frame makes the interface simple,
// but may have performance implications on some compositors?
// We might need to change the interface to maintain a visual
// tree that can be mutated?
// TODO(gw): We might need to add a concept of a hierachy in future.
// TODO(gw): In future, expand to support a more complete transform matrix.
fn add_surface(
&mut self,
id: NativeSurfaceId,
position: DeviceIntPoint,
clip_rect: DeviceIntRect,
);
/// Commit any changes in the compositor tree for this frame. WR calls
/// this once when all surface and visual updates are complete, to signal
/// that the OS composite transaction should be applied.
fn end_frame(&mut self);
}

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

@ -1076,6 +1076,11 @@ pub enum DrawTarget {
fbo: FBOId,
size: FramebufferIntSize,
},
/// An OS compositor surface
NativeSurface {
offset: DeviceIntPoint,
dimensions: DeviceIntSize,
},
}
impl DrawTarget {
@ -1123,6 +1128,7 @@ impl DrawTarget {
DrawTarget::Default { total_size, .. } => DeviceIntSize::from_untyped(total_size.to_untyped()),
DrawTarget::Texture { dimensions, .. } => dimensions,
DrawTarget::External { size, .. } => DeviceIntSize::from_untyped(size.to_untyped()),
DrawTarget::NativeSurface { dimensions, .. } => dimensions,
}
}
@ -1135,6 +1141,9 @@ impl DrawTarget {
fb_rect.origin.x += rect.origin.x;
}
DrawTarget::Texture { .. } | DrawTarget::External { .. } => (),
DrawTarget::NativeSurface { .. } => {
panic!("bug: is this ever used for native surfaces?");
}
}
fb_rect
}
@ -1156,6 +1165,9 @@ impl DrawTarget {
.intersection(rect)
.unwrap_or_else(FramebufferIntRect::zero)
}
DrawTarget::NativeSurface { offset, .. } => {
FramebufferIntRect::from_untyped(&scissor_rect.translate(offset.to_vector()).to_untyped())
}
DrawTarget::Texture { .. } | DrawTarget::External { .. } => {
FramebufferIntRect::from_untyped(&scissor_rect.to_untyped())
}
@ -1201,6 +1213,9 @@ impl From<DrawTarget> for ReadTarget {
fn from(t: DrawTarget) -> Self {
match t {
DrawTarget::Default { .. } => ReadTarget::Default,
DrawTarget::NativeSurface { .. } => {
unreachable!("bug: native surfaces cannot be read targets");
}
DrawTarget::Texture { fbo_id, .. } =>
ReadTarget::Texture { fbo_id },
DrawTarget::External { fbo, .. } =>
@ -1704,19 +1719,35 @@ impl Device {
target: DrawTarget,
) {
let (fbo_id, rect, depth_available) = match target {
DrawTarget::Default { rect, .. } => (self.default_draw_fbo, rect, true),
DrawTarget::Default { rect, .. } => {
(Some(self.default_draw_fbo), rect, true)
}
DrawTarget::Texture { dimensions, fbo_id, with_depth, .. } => {
let rect = FramebufferIntRect::new(
FramebufferIntPoint::zero(),
FramebufferIntSize::from_untyped(dimensions.to_untyped()),
);
(fbo_id, rect, with_depth)
(Some(fbo_id), rect, with_depth)
},
DrawTarget::External { fbo, size } => (fbo, size.into(), false),
DrawTarget::External { fbo, size } => {
(Some(fbo), size.into(), false)
}
DrawTarget::NativeSurface { offset, dimensions, .. } => {
(
None,
FramebufferIntRect::new(
FramebufferIntPoint::from_untyped(offset.to_untyped()),
FramebufferIntSize::from_untyped(dimensions.to_untyped()),
),
true
)
}
};
self.depth_available = depth_available;
self.bind_draw_target_impl(fbo_id);
if let Some(fbo_id) = fbo_id {
self.bind_draw_target_impl(fbo_id);
}
self.gl.viewport(
rect.origin.x,
rect.origin.y,

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

@ -8,7 +8,7 @@ use api::units::*;
use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
use crate::clip::{ClipStore, ClipChainStack};
use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
use crate::composite::CompositeState;
use crate::composite::{CompositeMode, CompositeState};
use crate::debug_render::DebugItem;
use crate::gpu_cache::{GpuCache, GpuCacheHandle};
use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
@ -64,6 +64,7 @@ pub struct FrameBuilderConfig {
pub advanced_blend_is_coherent: bool,
pub batch_lookback_count: usize,
pub background_color: Option<ColorF>,
pub composite_mode: CompositeMode,
}
/// A set of common / global resources that are retained between
@ -479,7 +480,7 @@ impl FrameBuilder {
let output_size = scene.output_rect.size.to_i32();
let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
let mut composite_state = CompositeState::new();
let mut composite_state = CompositeState::new(scene.config.composite_mode);
let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
scene,
@ -842,7 +843,7 @@ pub fn build_render_pass(
let (target_rect, _) = task.get_target_rect();
match task.location {
RenderTaskLocation::PictureCache { texture, layer, .. } => {
RenderTaskLocation::PictureCache { ref surface, .. } => {
// TODO(gw): The interface here is a bit untidy since it's
// designed to support batch merging, which isn't
// relevant for picture cache targets. We
@ -862,8 +863,7 @@ pub fn build_render_pass(
debug_assert!(batch_containers.is_empty());
let target = PictureCacheTarget {
texture,
layer: layer as usize,
surface: surface.clone(),
clear_color,
alpha_batch_container,
};

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

@ -201,6 +201,7 @@ pub extern crate api;
extern crate webrender_build;
#[doc(hidden)]
pub use crate::composite::{Compositor, NativeSurfaceId};
pub use crate::device::{build_shader_strings, UploadMethod, VertexUsageHint, get_gl_target};
pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, FormatDesc};
pub use crate::device::Device;

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

@ -68,13 +68,14 @@ use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId};
use crate::clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX,
ClipScrollTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace, CoordinateSystemId
};
use crate::composite::{CompositeMode, CompositeState, NativeSurfaceId};
use crate::debug_colors;
use euclid::{vec3, Point2D, Scale, Size2D, Vector2D, Rect};
use euclid::approxeq::ApproxEq;
use crate::filterdata::SFilterData;
use crate::frame_builder::{FrameVisibilityContext, FrameVisibilityState};
use crate::intern::ItemUid;
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, PlaneSplitAnchor};
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, PlaneSplitAnchor, TextureSource};
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use crate::gpu_types::UvRectKind;
@ -339,6 +340,9 @@ struct TilePostUpdateContext<'a> {
struct TilePostUpdateState<'a> {
/// Allow access to the texture cache for requesting tiles
resource_cache: &'a ResourceCache,
/// Current configuration and setup for compositing all the picture cache tiles in renderer.
composite_state: &'a mut CompositeState,
}
/// Information about the dependencies of a single primitive instance.
@ -392,16 +396,84 @@ impl PrimitiveDependencyInfo {
}
}
/// A stable ID for a given tile, to help debugging.
/// A stable ID for a given tile, to help debugging. These are also used
/// as unique identfiers for tile surfaces when using a native compositor.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TileId(usize);
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct TileId(pub usize);
/// A descriptor for the kind of texture that a picture cache tile will
/// be drawn into.
#[derive(Debug)]
pub enum SurfaceTextureDescriptor {
/// When using the WR compositor, the tile is drawn into an entry
/// in the WR texture cache.
TextureCache {
handle: TextureCacheHandle
},
/// When using an OS compositor, the tile is drawn into a native
/// surface identified by arbitrary id.
NativeSurface {
/// The arbitrary id of this surface.
id: NativeSurfaceId,
/// Size in device pixels of the native surface.
size: DeviceIntSize,
},
}
/// This is the same as a `SurfaceTextureDescriptor` but has been resolved
/// into a texture cache handle (if appropriate) that can be used by the
/// batching and compositing code in the renderer.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ResolvedSurfaceTexture {
TextureCache {
/// The texture ID to draw to.
texture: TextureSource,
/// Slice index in the texture array to draw to.
layer: i32,
},
NativeSurface {
/// The arbitrary id of this surface.
id: NativeSurfaceId,
/// Size in device pixels of the native surface.
size: DeviceIntSize,
}
}
impl SurfaceTextureDescriptor {
/// Create a resolved surface texture for this descriptor
pub fn resolve(
&self,
resource_cache: &ResourceCache,
) -> ResolvedSurfaceTexture {
match self {
SurfaceTextureDescriptor::TextureCache { handle } => {
let cache_item = resource_cache.texture_cache.get(handle);
ResolvedSurfaceTexture::TextureCache {
texture: cache_item.texture_id,
layer: cache_item.texture_layer,
}
}
SurfaceTextureDescriptor::NativeSurface { id, size } => {
ResolvedSurfaceTexture::NativeSurface {
id: *id,
size: *size,
}
}
}
}
}
/// The backing surface for this tile.
#[derive(Debug)]
pub enum TileSurface {
Texture {
/// Handle to the texture cache entry which gets drawn to.
handle: TextureCacheHandle,
/// Descriptor for the surface that this tile draws into.
descriptor: SurfaceTextureDescriptor,
/// Bitfield specifying the dirty region(s) that are relevant to this tile.
visibility_mask: PrimitiveVisibilityMask,
},
@ -668,25 +740,40 @@ impl Tile {
return false;
}
// For small tiles, only allow splitting once, since otherwise we
// end up splitting into tiny dirty rects that aren't saving much
// in the way of pixel work.
let max_split_level = if ctx.current_tile_size == TILE_SIZE_LARGE {
3
} else {
1
};
// If we are using native composite mode, we don't currently support
// dirty rects and must update the entire tile. A simple way to
// achieve this is to never consider splitting the dirty rect!
// TODO(gw): Support dirty rect updates for native compositor mode
// on operating systems that support this.
// TODO(gw): Consider using smaller tiles and/or tile splits for
// native compositors that don't support dirty rects.
if state.composite_state.composite_mode == CompositeMode::Draw {
// For small tiles, only allow splitting once, since otherwise we
// end up splitting into tiny dirty rects that aren't saving much
// in the way of pixel work.
let max_split_level = if ctx.current_tile_size == TILE_SIZE_LARGE {
3
} else {
1
};
// Consider splitting / merging dirty regions
self.root.maybe_merge_or_split(
0,
&self.current_descriptor.prims,
max_split_level,
);
// Consider splitting / merging dirty regions
self.root.maybe_merge_or_split(
0,
&self.current_descriptor.prims,
max_split_level,
);
}
// See if this tile is a simple color, in which case we can just draw
// it as a rect, and avoid allocating a texture surface and drawing it.
let is_simple_prim = self.current_descriptor.prims.len() == 1 && self.is_opaque;
// TODO(gw): Initial native compositor interface doesn't support simple
// color tiles. We can definitely support this in DC, so this
// should be added as a follow up.
let is_simple_prim =
self.current_descriptor.prims.len() == 1 &&
self.is_opaque &&
state.composite_state.composite_mode == CompositeMode::Draw;
// Set up the backing surface for this tile.
let surface = if is_simple_prim {
@ -713,8 +800,30 @@ impl Tile {
old_surface
}
Some(TileSurface::Color { .. }) | Some(TileSurface::Clear) | None => {
// This is the case where we are constructing a tile surface that
// involves drawing to a texture. Create the correct surface
// descriptor depending on the compositing mode that will read
// the output.
let descriptor = match state.composite_state.composite_mode {
CompositeMode::Draw => {
// For a texture cache entry, create an invalid handle that
// will be allocated when update_picture_cache is called.
SurfaceTextureDescriptor::TextureCache {
handle: TextureCacheHandle::invalid(),
}
}
CompositeMode::Native => {
// For a new native OS surface, we need to queue up creation
// of a native surface to be passed to the compositor interface.
state.composite_state.create_surface(
NativeSurfaceId(self.id.0 as u64),
ctx.current_tile_size,
)
}
};
TileSurface::Texture {
handle: TextureCacheHandle::invalid(),
descriptor,
visibility_mask: PrimitiveVisibilityMask::empty(),
}
}
@ -1290,6 +1399,11 @@ impl TileCacheInstance {
// threshold above. If we ever see this happening we can improve
// the theshold logic above.
if desired_tile_size != self.current_tile_size {
// Destroy any native surfaces on the tiles that will be dropped due
// to resizing.
frame_state.composite_state.destroy_native_surfaces(
self.tiles.values(),
);
self.tiles.clear();
self.current_tile_size = desired_tile_size;
}
@ -1447,6 +1561,14 @@ impl TileCacheInstance {
}
}
// Any old tiles that remain after the loop above are going to be dropped. For
// simple composite mode, the texture cache handle will expire and be collected
// by the texture cache. For native compositor mode, we need to explicitly
// invoke a callback to the client to destroy that surface.
frame_state.composite_state.destroy_native_surfaces(
old_tiles.values(),
);
world_culling_rect
}
@ -1763,6 +1885,7 @@ impl TileCacheInstance {
let mut state = TilePostUpdateState {
resource_cache: frame_state.resource_cache,
composite_state: frame_state.composite_state,
};
// Step through each tile and invalidate if the dependencies have changed.
@ -3038,7 +3161,7 @@ impl PicturePrimitive {
}
}
if let TileSurface::Texture { ref handle, .. } = surface {
if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::TextureCache { ref handle, .. }, .. } = surface {
// Invalidate if the backing texture was evicted.
if frame_state.resource_cache.texture_cache.is_allocated(handle) {
// Request the backing texture so it won't get evicted this frame.
@ -3066,13 +3189,15 @@ impl PicturePrimitive {
}
// Ensure that this texture is allocated.
if let TileSurface::Texture { ref mut handle, ref mut visibility_mask } = surface {
if !frame_state.resource_cache.texture_cache.is_allocated(handle) {
frame_state.resource_cache.texture_cache.update_picture_cache(
tile_cache.current_tile_size,
handle,
frame_state.gpu_cache,
);
if let TileSurface::Texture { ref mut descriptor, ref mut visibility_mask } = surface {
if let SurfaceTextureDescriptor::TextureCache { ref mut handle } = descriptor {
if !frame_state.resource_cache.texture_cache.is_allocated(handle) {
frame_state.resource_cache.texture_cache.update_picture_cache(
tile_cache.current_tile_size,
handle,
frame_state.gpu_cache,
);
}
}
*visibility_mask = PrimitiveVisibilityMask::empty();
@ -3115,13 +3240,12 @@ impl PicturePrimitive {
// CPUs). Round the rect here before casting to integer device pixels
// to ensure the scissor rect is correct.
let scissor_rect = (scissor_rect * device_pixel_scale).round();
let cache_item = frame_state.resource_cache.texture_cache.get(handle);
let surface = descriptor.resolve(frame_state.resource_cache);
let task = RenderTask::new_picture(
RenderTaskLocation::PictureCache {
texture: cache_item.texture_id,
layer: cache_item.texture_layer,
size: tile_cache.current_tile_size,
surface,
},
tile_cache.current_tile_size.to_f32(),
pic_index,
@ -4522,3 +4646,26 @@ impl TileNode {
}
}
}
impl CompositeState {
// A helper function to destroy all native surfaces for a given list of tiles
fn destroy_native_surfaces<'a, I: Iterator<Item = &'a Tile>>(
&mut self,
tiles_iter: I,
) {
// Any old tiles that remain after the loop above are going to be dropped. For
// simple composite mode, the texture cache handle will expire and be collected
// by the texture cache. For native compositor mode, we need to explicitly
// invoke a callback to the client to destroy that surface.
if let CompositeMode::Native = self.composite_mode {
for tile in tiles_iter {
// Only destroy native surfaces that have been allocated. It's
// possible for display port tiles to be created that never
// come on screen, and thus never get a native surface allocated.
if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::NativeSurface { id, .. }, .. }) = tile.surface {
self.destroy_surface(id);
}
}
}
}
}

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

@ -16,7 +16,7 @@ use crate::gpu_cache::{GpuCache, GpuCacheAddress};
use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
use crate::gpu_types::{TransformPalette, ZBufferIdGenerator};
use crate::internal_types::{FastHashMap, TextureSource, LayerIndex, Swizzle, SavedTargetIndex};
use crate::picture::SurfaceInfo;
use crate::picture::{SurfaceInfo, ResolvedSurfaceTexture};
use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer, PrimitiveVisibilityMask};
use crate::prim_store::gradient::GRADIENT_FP_STOPS;
use crate::render_backend::DataStores;
@ -719,8 +719,7 @@ impl RenderTarget for AlphaRenderTarget {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PictureCacheTarget {
pub texture: TextureSource,
pub layer: usize,
pub surface: ResolvedSurfaceTexture,
pub alpha_batch_container: AlphaBatchContainer,
pub clear_color: Option<ColorF>,
}

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

@ -11,7 +11,8 @@ use crate::filterdata::SFilterData;
use crate::frame_builder::FrameBuilderConfig;
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind};
use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex, TextureSource};
use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex};
use crate::picture::ResolvedSurfaceTexture;
use crate::prim_store::{PictureIndex, PrimitiveVisibilityMask};
use crate::prim_store::image::ImageCacheKey;
use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientStopKey};
@ -74,10 +75,8 @@ pub enum RenderTaskLocation {
/// This render task will be drawn to a picture cache texture that is
/// persisted between both frames and scenes, if the content remains valid.
PictureCache {
/// The texture ID to draw to.
texture: TextureSource,
/// Slice index in the texture array to draw to.
layer: i32,
/// Describes either a WR texture or a native OS compositor target
surface: ResolvedSurfaceTexture,
/// Size in device pixels of this picture cache tile.
size: DeviceIntSize,
},
@ -107,7 +106,9 @@ impl RenderTaskLocation {
RenderTaskLocation::Dynamic(None, _) => panic!("Expected position to be set for the task!"),
RenderTaskLocation::Dynamic(Some((origin, layer)), size) => (DeviceIntRect::new(origin, size), layer.0 as LayerIndex),
RenderTaskLocation::TextureCache { rect, layer, .. } => (rect, layer),
RenderTaskLocation::PictureCache { layer, size, .. } => (size.into(), layer as LayerIndex),
RenderTaskLocation::PictureCache { .. } => {
panic!("bug: picture cache tasks should never be a source!");
}
}
}
}
@ -1354,7 +1355,12 @@ impl RenderTask {
RenderTaskLocation::TextureCache {layer, rect, .. } => {
(rect, RenderTargetIndex(layer as usize))
}
RenderTaskLocation::PictureCache { size, layer, .. } => {
RenderTaskLocation::PictureCache { ref surface, size, .. } => {
let layer = match surface {
ResolvedSurfaceTexture::TextureCache { layer, .. } => *layer,
ResolvedSurfaceTexture::NativeSurface { .. } => 0,
};
(
DeviceIntRect::new(
DeviceIntPoint::zero(),

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

@ -47,7 +47,8 @@ use api::channel::{MsgSender, PayloadReceiverHelperMethods};
use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList};
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile};
use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, CompositeMode, Compositor};
use crate::composite::{NativeSurfaceOperationDetails};
use crate::debug_colors;
use crate::debug_render::{DebugItem, DebugRenderer};
use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO};
@ -70,7 +71,7 @@ use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSe
use crate::internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource};
use crate::internal_types::{RenderTargetInfo, SavedTargetIndex, Swizzle};
use malloc_size_of::MallocSizeOfOps;
use crate::picture::{RecordedDirtyRegion, TILE_SIZE_LARGE, TILE_SIZE_SMALL};
use crate::picture::{RecordedDirtyRegion, TILE_SIZE_LARGE, TILE_SIZE_SMALL, ResolvedSurfaceTexture};
use crate::prim_store::DeferredResolve;
use crate::profiler::{BackendProfileCounters, FrameProfileCounters, TimeProfileCounter,
GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
@ -1875,6 +1876,9 @@ pub struct Renderer {
/// If true, partial present state has been reset and everything needs to
/// be drawn on the next render.
force_redraw: bool,
/// An optional client provided interface to a native OS compositor
native_compositor: Option<Box<dyn Compositor>>,
}
#[derive(Debug)]
@ -2131,6 +2135,11 @@ impl Renderer {
(false, _) => FontRenderMode::Mono,
};
let composite_mode = match options.native_compositor {
Some(..) => CompositeMode::Native,
None => CompositeMode::Draw,
};
let config = FrameBuilderConfig {
default_font_render_mode,
dual_source_blending_is_enabled: true,
@ -2143,6 +2152,7 @@ impl Renderer {
advanced_blend_is_coherent: ext_blend_equation_advanced_coherent,
batch_lookback_count: options.batch_lookback_count,
background_color: options.clear_color,
composite_mode,
};
info!("WR {:?}", config);
@ -2381,6 +2391,7 @@ impl Renderer {
documents_seen: FastHashSet::default(),
present_config,
force_redraw: true,
native_compositor: options.native_compositor,
};
// We initially set the flags to default and then now call set_debug_flags
@ -3910,8 +3921,11 @@ impl Renderer {
CompositeTileSurface::Clear => {
(TextureSource::Dummy, 0.0, ColorF::BLACK)
}
CompositeTileSurface::Texture { texture_id, texture_layer } => {
(texture_id, texture_layer as f32, ColorF::WHITE)
CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => {
(texture, layer as f32, ColorF::WHITE)
}
CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::NativeSurface { .. } } => {
unreachable!("bug: found native surface in simple composite path");
}
};
let textures = BatchTextures::color(texture);
@ -3967,7 +3981,7 @@ impl Renderer {
/// the only way that picture cache tiles get drawn. In future, the tiles
/// will often be handed to the OS compositor, and this method will be
/// rarely used.
fn composite(
fn composite_simple(
&mut self,
composite_state: &CompositeState,
clear_framebuffer: bool,
@ -4151,6 +4165,9 @@ impl Renderer {
}
let clear_rect = match draw_target {
DrawTarget::NativeSurface { .. } => {
unreachable!("bug: native compositor surface in child target");
}
DrawTarget::Default { rect, total_size } if rect.origin == FramebufferIntPoint::zero() && rect.size == total_size => {
// whole screen is covered, no need for scissor
None
@ -4858,6 +4875,36 @@ impl Renderer {
debug_assert!(self.texture_resolver.prev_pass_color.is_none());
}
fn update_native_surfaces(
&mut self,
composite_state: &CompositeState,
) {
match self.native_compositor {
Some(ref mut compositor) => {
for op in &composite_state.native_surface_updates {
match op.details {
NativeSurfaceOperationDetails::CreateSurface { size } => {
compositor.create_surface(
op.id,
size,
);
}
NativeSurfaceOperationDetails::DestroySurface => {
compositor.destroy_surface(
op.id,
);
}
}
}
}
None => {
// Ensure nothing is added in simple composite mode, since otherwise
// memory will leak as this doesn't get drained
debug_assert!(composite_state.native_surface_updates.is_empty());
}
}
}
fn draw_frame(
&mut self,
frame: &mut Frame,
@ -4880,6 +4927,7 @@ impl Renderer {
self.device.disable_stencil();
self.bind_frame_data(frame);
self.update_native_surfaces(&frame.composite_state);
for (_pass_index, pass) in frame.passes.iter_mut().enumerate() {
#[cfg(not(target_os = "android"))]
@ -4922,13 +4970,22 @@ impl Renderer {
};
if self.enable_picture_caching {
self.composite(
&frame.composite_state,
clear_framebuffer,
draw_target,
&projection,
results,
);
// If we have a native OS compositor, then make use of that interface
// to specify how to composite each of the picture cache surfaces.
match self.native_compositor {
Some(ref mut interface) => {
frame.composite_state.composite_native(&mut **interface);
}
None => {
self.composite_simple(
&frame.composite_state,
clear_framebuffer,
draw_target,
&projection,
results,
);
}
}
} else {
if clear_framebuffer {
let clear_color = self.clear_color.map(|color| color.to_array());
@ -4980,14 +5037,30 @@ impl Renderer {
for picture_target in picture_cache {
results.stats.color_target_count += 1;
let (texture, _) = self.texture_resolver
.resolve(&picture_target.texture)
.expect("bug");
let draw_target = DrawTarget::from_texture(
texture,
picture_target.layer,
true,
);
let draw_target = match picture_target.surface {
ResolvedSurfaceTexture::TextureCache { ref texture, layer } => {
let (texture, _) = self.texture_resolver
.resolve(texture)
.expect("bug");
DrawTarget::from_texture(
texture,
layer as usize,
true,
)
}
ResolvedSurfaceTexture::NativeSurface { id, size, .. } => {
let offset = self.native_compositor
.as_mut()
.expect("bug: no native compositor")
.bind(id);
DrawTarget::NativeSurface {
offset,
dimensions: size,
}
}
};
let projection = Transform3D::ortho(
0.0,
@ -5006,6 +5079,14 @@ impl Renderer {
&frame.render_tasks,
&mut results.stats,
);
// Native OS surfaces must be unbound at the end of drawing to them
if let ResolvedSurfaceTexture::NativeSurface { .. } = picture_target.surface {
self.native_compositor
.as_mut()
.expect("bug: no native compositor")
.unbind();
}
}
}
@ -5900,6 +5981,8 @@ pub struct RendererOptions {
pub dump_shader_source: Option<String>,
/// An optional presentation config for compositor integration.
pub present_config: Option<PresentConfig>,
/// An optional client provided interface to a native / OS compositor.
pub native_compositor: Option<Box<dyn Compositor>>,
}
impl Default for RendererOptions {
@ -5952,6 +6035,7 @@ impl Default for RendererOptions {
start_debug_server: true,
dump_shader_source: None,
present_config: None,
native_compositor: None,
}
}
}
@ -6477,3 +6561,36 @@ fn should_skip_batch(kind: &BatchKind, flags: &DebugFlags) -> bool {
_ => false,
}
}
impl CompositeState {
/// Use the client provided native compositor interface to add all picture
/// cache tiles to the OS compositor
fn composite_native(
&self,
compositor: &mut dyn Compositor,
) {
// Inform the client that we are starting a composition transaction
compositor.begin_frame();
// For each tile, update the properties with the native OS compositor,
// such as position and clip rect. z-order of the tiles are implicit based
// on the order they are added in this loop.
for tile in self.opaque_tiles.iter().chain(self.alpha_tiles.iter()) {
// Extract the native surface id. We should only ever encounter native surfaces here!
let id = match tile.surface {
CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::NativeSurface { id, .. }, .. } => id,
_ => unreachable!(),
};
// Add the tile to the OS compositor.
compositor.add_surface(
id,
tile.rect.origin.to_i32(),
tile.clip_rect.to_i32(),
);
}
// Inform the client that we are finished this composition transaction
compositor.end_frame();
}
}

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

@ -5,6 +5,7 @@
use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, FontRenderMode};
use api::{PipelineId, PropertyBinding, PropertyBindingId, MixBlendMode, StackingContext};
use api::units::*;
use crate::composite::CompositeMode;
use crate::clip::{ClipStore, ClipDataStore};
use crate::clip_scroll_tree::ClipScrollTree;
use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig};
@ -251,6 +252,7 @@ impl BuiltScene {
advanced_blend_is_coherent: false,
batch_lookback_count: 0,
background_color: None,
composite_mode: CompositeMode::Draw,
},
}
}