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