/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::units::*; use api::{ColorF, ImageBufferKind, ImageRendering, PremultipliedColorF}; usecrate::batch::BatchTextures; usecrate::composite::{
CompositeState, CompositeSurfaceFormat, CompositeTileSurface, CompositorConfig,
CompositorInputLayer, CompositorSurfaceUsage, CompositorSurfaceTransform,
CompositeRoundedCorner, NativeTileId, ResolvedExternalSurface,
ResolvedExternalSurfaceColorData, ClipRadius, CompositeFeatures, CompositorKind, TileKind,
}; usecrate::frame_builder::Frame; usecrate::{CompositorInputConfig, PictureCacheDebugInfo, debug_colors}; use api::{ClipMode, DebugFlags}; use std::collections::HashSet; use std::mem; usecrate::debug_item::DebugItem; usecrate::segment::EdgeMask; usecrate::device::DrawTarget; usecrate::gpu_types::{CompositeInstance, ZBufferId}; usecrate::internal_types::{FastHashMap, TextureSource}; usecrate::picture::ResolvedSurfaceTexture; usesuper::RenderResults; usecrate::profiler::{self}; usecrate::rectangle_occlusion as occlusion; usecrate::renderer::{
GPU_SAMPLER_TAG_OPAQUE, GPU_SAMPLER_TAG_TRANSPARENT, GPU_TAG_COMPOSITE, PartialPresentMode,
}; usecrate::renderer::{FramebufferKind, Renderer, RendererStats, VertexArrayKind}; usecrate::segment::SegmentBuilder; usecrate::tile_cache::TileId; use euclid::{Scale, Transform3D, default};
for surface in external_surfaces { // See if this surface needs to be updated let (native_surface_id, surface_size) = match surface.update_params {
Some(params) => params,
None => continue,
};
// When updating an external surface, the entire surface rect is used // for all of the draw, dirty, valid and clip rect parameters. let surface_rect = surface_size.into();
// Bind the native compositor surface to update let surface_info = self.compositor_config
.compositor()
.unwrap()
.bind(
&mutself.device,
NativeTileId {
surface_id: native_surface_id,
x: 0,
y: 0,
},
surface_rect,
surface_rect,
);
// Bind the native surface to current FBO target let draw_target = DrawTarget::NativeSurface {
offset: surface_info.origin,
external_fbo_id: surface_info.fbo_id,
dimensions: surface_size,
}; self.device.bind_draw_target(draw_target);
let projection = Transform3D::ortho( 0.0,
surface_size.width as f32, 0.0,
surface_size.height as f32, self.device.ortho_near_plane(), self.device.ortho_far_plane(),
);
let ( textures, instance ) = match surface.color_data {
ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, channel_bit_depth, .. } => {
let textures = BatchTextures::composite_yuv(
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.input.colors[0], planes[0].uv_rect), self.texture_resolver.get_uv_rect(&textures.input.colors[1], planes[1].uv_rect), self.texture_resolver.get_uv_rect(&textures.input.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.
color_space,
format,
channel_bit_depth,
uv_rects,
(false, false),
None,
);
// Bind an appropriate YUV shader for the texture format kind self.shaders
.borrow_mut()
.get_composite_shader(
CompositeSurfaceFormat::Yuv,
surface.image_buffer_kind,
instance.get_yuv_features(),
).bind(
&mutself.device,
&projection,
None,
&mutself.renderer_errors,
&mutself.profile,
&mutself.command_log,
);
( textures, instance )
},
ResolvedExternalSurfaceColorData::Rgb{ ref plane, .. } => { let textures = BatchTextures::composite_rgb(plane.texture); let uv_rect = self.texture_resolver.get_uv_rect(&textures.input.colors[0], plane.uv_rect); let instance = CompositeInstance::new_rgb(
surface_rect.to_f32(),
surface_rect.to_f32(),
PremultipliedColorF::WHITE,
uv_rect,
plane.texture.uses_normalized_uvs(),
(false, false),
None,
); let features = instance.get_rgb_features();
/// Draw a list of tiles to the framebuffer fn draw_tile_list<'a, I: Iterator<Item = &'a occlusion::Item<OcclusionItemKey>>>(
&mutself,
tiles_iter: I,
composite_state: &CompositeState,
external_surfaces: &[ResolvedExternalSurface],
projection: &default::Transform3D<f32>,
stats: &mut RendererStats,
) { letmut current_shader_params = (
CompositeSurfaceFormat::Rgba,
ImageBufferKind::Texture2D,
CompositeFeatures::empty(),
None,
); letmut current_textures = BatchTextures::empty(); letmut instances = Vec::new();
for item in tiles_iter { let tile = &composite_state.tiles[item.key.tile_index];
let clip_rect = item.rectangle; let tile_rect = composite_state.get_device_rect(&tile.local_rect, tile.transform_index); let transform = composite_state.get_device_transform(tile.transform_index); let flip = (transform.scale.x < 0.0, transform.scale.y < 0.0);
let clip = if item.key.needs_mask {
tile.clip_index.map(|index| {
composite_state.get_compositor_clip(index)
})
} else {
None
};
// Work out the draw params based on the tile surface let (instance, textures, shader_params) = match tile.surface {
CompositeTileSurface::Color { color } => { let dummy = TextureSource::Dummy; let image_buffer_kind = dummy.image_buffer_kind(); let instance = CompositeInstance::new(
tile_rect,
clip_rect,
color.premultiplied(),
flip,
clip,
); let features = instance.get_rgb_features();
(
instance,
BatchTextures::composite_rgb(dummy),
(CompositeSurfaceFormat::Rgba, image_buffer_kind, features, None),
)
}
CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture } } => { let instance = CompositeInstance::new(
tile_rect,
clip_rect,
PremultipliedColorF::WHITE,
flip,
clip,
); let features = instance.get_rgb_features();
(
instance,
BatchTextures::composite_rgb(texture),
(
CompositeSurfaceFormat::Rgba,
ImageBufferKind::Texture2D,
features,
None,
),
)
}
CompositeTileSurface::ExternalSurface { external_surface_index } => { let surface = &external_surfaces[external_surface_index.0];
match surface.color_data {
ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, channel_bit_depth, .. } => { let textures = BatchTextures::composite_yuv(
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.input.colors[0], planes[0].uv_rect), self.texture_resolver.get_uv_rect(&textures.input.colors[1], planes[1].uv_rect), self.texture_resolver.get_uv_rect(&textures.input.colors[2], planes[2].uv_rect),
];
let instance = CompositeInstance::new_yuv(
tile_rect,
clip_rect,
color_space,
format,
channel_bit_depth,
uv_rects,
flip,
clip,
); let features = instance.get_yuv_features();
// Add instance to current batch
instances.push(instance);
}
// Flush the last batch if !instances.is_empty() { self.shaders
.borrow_mut()
.get_composite_shader(
current_shader_params.0,
current_shader_params.1,
current_shader_params.2,
).bind(
&mutself.device,
projection,
current_shader_params.3,
&mutself.renderer_errors,
&mutself.profile,
&mutself.command_log,
); self.draw_instanced_batch(
&instances,
VertexArrayKind::Composite,
¤t_textures,
stats,
);
}
}
// Composite tiles in a swapchain. When using LayerCompositor, we may // split the compositing in to multiple swapchains. fn composite_pass(
&mutself,
composite_state: &CompositeState,
draw_target: DrawTarget,
clear_color: ColorF,
projection: &default::Transform3D<f32>,
results: &mut RenderResults,
partial_present_mode: Option<PartialPresentMode>,
layer: &SwapChainLayer,
) { self.device.bind_draw_target(draw_target); self.device.disable_depth_write(); self.device.disable_depth();
// If using KHR_partial_update, call eglSetDamageRegion. // This must be called exactly once per frame, and prior to any rendering to the main // framebuffer. Additionally, on Mali-G77 we encountered rendering issues when calling // this earlier in the frame, during offscreen render passes. So call it now, immediately // before rendering to the main framebuffer. See bug 1685276 for details. iflet Some(partial_present) = self.compositor_config.partial_present() { iflet Some(PartialPresentMode::Single { dirty_rect }) = partial_present_mode {
partial_present.set_buffer_damage_region(&[dirty_rect.to_i32()]);
}
}
// Clear the framebuffer let clear_color = Some(clear_color.to_array());
match partial_present_mode {
Some(PartialPresentMode::Single { dirty_rect }) => { // There is no need to clear if the dirty rect is occluded. Additionally, // on Mali-G77 we have observed artefacts when calling glClear (even with // the empty scissor rect set) after calling eglSetDamageRegion with an // empty damage region. So avoid clearing in that case. See bug 1709548. if !dirty_rect.is_empty() && layer.occlusion.test(&dirty_rect) { // We have a single dirty rect, so clear only that self.device.clear_target(clear_color,
None,
Some(draw_target.to_framebuffer_rect(dirty_rect.to_i32())));
}
}
None => { // Partial present is disabled, so clear the entire framebuffer self.device.clear_target(clear_color,
None,
None);
}
}
// Draw opaque tiles let opaque_items = layer.occlusion.opaque_items(); if !opaque_items.is_empty() { let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE); self.set_blend(false, FramebufferKind::Main); self.draw_tile_list(
opaque_items.iter(),
&composite_state,
&composite_state.external_surfaces,
projection,
&mut results.stats,
); self.gpu_profiler.finish_sampler(opaque_sampler);
}
// Draw alpha tiles let alpha_items = layer.occlusion.alpha_items(); if !alpha_items.is_empty() { let transparent_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); self.set_blend(true, FramebufferKind::Main); self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main); self.draw_tile_list(
alpha_items.iter().rev(),
&composite_state,
&composite_state.external_surfaces,
projection,
&mut results.stats,
); self.gpu_profiler.finish_sampler(transparent_sampler);
}
}
/// Composite picture cache tiles into the framebuffer. This is currently /// 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_simple(
&mutself,
composite_state: &CompositeState,
frame_device_size: DeviceIntSize,
fb_draw_target: DrawTarget,
projection: &default::Transform3D<f32>,
results: &mut RenderResults,
partial_present_mode: Option<PartialPresentMode>,
device_size: DeviceIntSize,
) { let _gm = self.gpu_profiler.start_marker("framebuffer"); let _timer = self.gpu_profiler.start_timer(GPU_TAG_COMPOSITE);
let num_tiles = composite_state.tiles.len(); self.profile.set(profiler::PICTURE_TILES, num_tiles);
// NOTE: Tiles here are being iterated in front-to-back order by // z-id, due to the sort in composite_state.end_frame() for (idx, tile) in composite_state.tiles.iter().enumerate() { let device_tile_box = composite_state.get_device_rect(
&tile.local_rect,
tile.transform_index
);
// Simple compositor needs the valid rect in device space to match clip rect let device_valid_rect = composite_state
.get_device_rect(&tile.local_valid_rect, tile.transform_index);
let rect = device_tile_box
.intersection_unchecked(&tile.device_clip_rect)
.intersection_unchecked(&device_valid_rect);
if rect.is_empty() { continue;
}
letmut disable_external_composite = enable_screenshot; iflet CompositeTileSurface::ExternalSurface { .. } = tile.surface { let transformed_rect = composite_state.get_device_rect(
&tile.local_rect,
tile.transform_index
); iflet None = transformed_rect.try_cast::<i16>() { // Disable external composite when rect is big.
disable_external_composite = true;
}
}
// Determine if the tile is an external surface or content let usage = match tile.surface {
CompositeTileSurface::Texture { .. } |
CompositeTileSurface::Color { .. } => {
CompositorSurfaceUsage::Content
}
CompositeTileSurface::ExternalSurface { external_surface_index } => { match (self.current_compositor_kind, disable_external_composite) {
(CompositorKind::Native { .. }, _) | (CompositorKind::Draw { .. }, _) => {
CompositorSurfaceUsage::Content
}
(CompositorKind::Layer { .. }, true) => {
CompositorSurfaceUsage::Content
}
(CompositorKind::Layer { .. }, false) => { let surface = &composite_state.external_surfaces[external_surface_index.0];
// TODO(gwc): For now, we only select a hardware overlay swapchain if we // have an external image, but it may make sense to do for compositor // surfaces without in future. match surface.external_image_id {
Some(external_image_id) => { let image_key = match surface.color_data {
ResolvedExternalSurfaceColorData::Rgb { image_dependency, .. } => image_dependency.key,
ResolvedExternalSurfaceColorData::Yuv { image_dependencies, .. } => image_dependencies[0].key,
};
// Determine whether we need a new layer, and if so, what kind let new_layer_kind = match input_layers.last() {
Some(curr_layer) => { match (curr_layer.usage, usage) { // Content -> content, composite in to same layer
(CompositorSurfaceUsage::Content, CompositorSurfaceUsage::Content) => None,
(CompositorSurfaceUsage::External { .. }, CompositorSurfaceUsage::Content) => Some(usage),
// Switch of layer type, or video -> video, need new swapchain
(CompositorSurfaceUsage::Content, CompositorSurfaceUsage::External { .. }) |
(CompositorSurfaceUsage::External { .. }, CompositorSurfaceUsage::External { .. }) => { // Only create a new layer if we're using LayerCompositor matchself.compositor_config {
CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => None,
CompositorConfig::Layer { .. } => {
Some(usage)
}
}
}
(CompositorSurfaceUsage::DebugOverlay, _) => {
Some(usage)
} // Should not encounter debug layers as new layer
(_, CompositorSurfaceUsage::DebugOverlay) => {
unreachable!();
}
}
}
None => { // No layers yet, so we need a new one
Some(usage)
}
};
iflet Some(new_layer_kind) = new_layer_kind { let (offset, clip_rect, is_opaque, rounded_clip_rect, rounded_clip_radii) = match usage {
CompositorSurfaceUsage::Content => {
(
DeviceIntPoint::zero(),
device_size.into(), false, // Assume not opaque, we'll calculate this later
device_size.into(),
ClipRadius::EMPTY,
)
}
CompositorSurfaceUsage::External { .. } => { let rect = composite_state.get_device_rect(
&tile.local_rect,
tile.transform_index
);
let clip_rect = tile.device_clip_rect.to_i32(); let is_opaque = tile.kind != TileKind::Alpha;
if window_is_opaque { match input_layers.last_mut() {
Some(_layer) => { // If the window is opaque, and the last(back) layer is // a content layer then mark that as opaque. // TODO: This causes talos performance regressions. // if let CompositorSurfaceUsage::Content = layer.usage { // layer.is_opaque = true; // }
}
None => { // If no tiles were present, and we expect an opaque window, // add an empty layer to force a composite that clears the screen, // to match existing semantics.
input_layers.push(CompositorInputLayer {
usage: CompositorSurfaceUsage::Content,
is_opaque: true,
offset: DeviceIntPoint::zero(),
clip_rect: device_size.into(),
rounded_clip_rect: device_size.into(),
rounded_clip_radii: ClipRadius::EMPTY,
});
// Recalculate dirty rect for layer compositor iflet Some(ref _compositor) = self.compositor_config.layer_compositor() { // Set visible rests of current frame to each tile's CompositeTileState. for item in full_render_occlusion
.opaque_items()
.iter()
.chain(full_render_occlusion.alpha_items().iter()) { let tile = &composite_state.tiles[item.key.tile_index]; match tile.tile_id {
Some(tile_id) => { iflet Some(tile_state) = layer_compositor_frame_state.tile_states.get_mut(&tile_id) {
tile_state.visible_rects.push(item.rectangle);
} else {
unreachable!();
}
}
None => {}
}
}
let can_use_partial_present =
!self.force_redraw && !full_render && self.layer_compositor_frame_state_in_prev_frame.is_some();
if can_use_partial_present { letmut combined_dirty_rect = DeviceRect::zero();
for tile in composite_state.tiles.iter() { if tile.tile_id.is_none() { match tile.surface {
CompositeTileSurface::ExternalSurface { .. } => {}
CompositeTileSurface::Texture { .. } |
CompositeTileSurface::Color { .. } => {
unreachable!();
},
} continue;
}
assert!(tile.tile_id.is_some());
let tiles_exists_in_prev_frame = self.layer_compositor_frame_state_in_prev_frame
.as_ref()
.unwrap()
.tile_states
.contains_key(&tile.tile_id.unwrap()); let tile_id = tile.tile_id.unwrap(); let tile_state = layer_compositor_frame_state.tile_states.get(&tile_id).unwrap();
if tiles_exists_in_prev_frame { let prev_tile_state = self.layer_compositor_frame_state_in_prev_frame
.as_ref()
.unwrap()
.tile_states
.get(&tile_id)
.unwrap();
if tile_state.same_state(prev_tile_state) { // Case that tile is same state in previous frame and current frame. // Intersection of tile's dirty rect and tile's visible rects are actual dirty rects. let dirty_rect = composite_state.get_device_rect(
&tile.local_dirty_rect,
tile.transform_index,
); for rect in tile_state.visible_rects.iter() { let visible_dirty_rect = rect.intersection(&dirty_rect); if visible_dirty_rect.is_some() {
combined_dirty_rect = combined_dirty_rect.union(&visible_dirty_rect.unwrap());
}
}
} else { // If tile is rendered in previous frame, but its state is different, // both visible rects in previous frame and current frame are dirty rects. for rect in tile_state.visible_rects
.iter()
.chain(prev_tile_state.visible_rects.iter()) {
combined_dirty_rect = combined_dirty_rect.union(&rect);
}
}
} else { // If tile is not rendered in previous frame, its all visible rects are dirty rects. for rect in &tile_state.visible_rects {
combined_dirty_rect = combined_dirty_rect.union(&rect);
}
}
}
// Case that tile is rendered in pervious frame, but not in current frame. for (tile_id, tile_state) inself.layer_compositor_frame_state_in_prev_frame
.as_ref()
.unwrap()
.tile_states
.iter() { if !layer_compositor_frame_state.tile_states.contains_key(&tile_id) { for rect in tile_state.visible_rects.iter() {
combined_dirty_rect = combined_dirty_rect.union(&rect);
}
}
}
// Case that ExternalSurface is not promoted to external composite. for rect in layer_compositor_frame_state
.rects_without_id
.iter()
.chain(self.layer_compositor_frame_state_in_prev_frame.as_ref().unwrap().rects_without_id.iter()) {
combined_dirty_rect = combined_dirty_rect.union(&rect);
}
let device_rect = DeviceRect::from_size(device_size.to_f32()); let clipped_dirty_rect = combined_dirty_rect.intersection_unchecked(&device_rect);
// NOTE: Tiles here are being iterated in front-to-back order by // z-id, due to the sort in composite_state.end_frame() for (idx, tile) in composite_state.tiles.iter().enumerate() { let device_tile_box = composite_state.get_device_rect(
&tile.local_rect,
tile.transform_index
);
// Determine a clip rect to apply to this tile, depending on what // the partial present mode is. let partial_clip_rect = match partial_present_mode {
Some(PartialPresentMode::Single { dirty_rect }) => dirty_rect,
None => device_tile_box,
};
// Simple compositor needs the valid rect in device space to match clip rect let device_valid_rect = composite_state
.get_device_rect(&tile.local_valid_rect, tile.transform_index);
let rect = device_tile_box
.intersection_unchecked(&tile.device_clip_rect)
.intersection_unchecked(&partial_clip_rect)
.intersection_unchecked(&device_valid_rect);
if rect.is_empty() { continue;
}
let layer_index = match tile_index_to_layer_index[idx] {
None => { // The rect of partial present should be subset of the rect of full render.
error!("rect {:?} should have valid layer index", rect); continue;
}
Some(layer_index) => layer_index,
};
// For normal tiles, add to occlusion tracker let layer = &mut swapchain_layers[layer_index];
let is_opaque = tile.kind == TileKind::Opaque;
match tile.clip_index {
Some(clip_index) => { let clip = composite_state.get_compositor_clip(clip_index);
// TODO(gw): Make segment builder generic on unit to avoid casts below.
segment_builder.initialize(
rect.cast_unit(),
None,
rect.cast_unit(),
);
segment_builder.push_clip_rect(
clip.rect.cast_unit(),
Some(clip.radius),
ClipMode::Clip,
);
segment_builder.build(|segment| { let key = OcclusionItemKey { tile_index: idx, needs_mask: segment.has_mask };
DrawTarget::NativeSurface {
offset: -layer.offset,
external_fbo_id: 0,
dimensions: frame_device_size,
}
} // Native can be hit when switching compositors (disable when using Layer)
CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => {
fb_draw_target
}
};
// TODO(gwc): When supporting external attached swapchains, need to skip the composite pass here
// Draw each compositing pass in to a swap chain self.composite_pass(
composite_state,
draw_target,
clear_color,
projection,
results,
partial_present_mode,
swapchain_layer,
);
// End frame notify for experimental compositor iflet Some(refmut compositor) = self.compositor_config.layer_compositor() { for (layer_index, layer) in input_layers.iter().enumerate() { // External surfaces need transform applied, but content // surfaces are always at identity let transform = match layer.usage {
CompositorSurfaceUsage::Content => CompositorSurfaceTransform::identity(),
CompositorSurfaceUsage::External { transform_index, .. } => composite_state.get_compositor_transform(transform_index),
CompositorSurfaceUsage::DebugOverlay => CompositorSurfaceTransform::identity(),
};
// If we have a native OS compositor, then make use of that interface // to specify how to composite each of the picture cache surfaces. matchself.current_compositor_kind {
CompositorKind::Native { .. } => { // We have already queued surfaces for early native composition by this point. // All that is left is to finally update any external native surfaces that were // invalidated so that composition can complete. self.update_external_native_surfaces(
&frame.composite_state.external_surfaces,
results,
);
}
CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => { self.composite_simple(
&frame.composite_state,
frame.device_rect.size(),
draw_target,
&projection,
results,
present_mode,
device_size,
);
}
} // Reset force_redraw. It was used in composite_simple() with layer compositor. self.force_redraw = false;
} else { // Rendering a frame without presenting it will confuse the partial // present logic, so force a full present for the next frame. self.force_redraw = true;
}
}
/// Update the dirty rects based on current compositing mode and config // TODO(gw): This can be tidied up significantly once the Draw compositor // is implemented in terms of the compositor trait. pub(super) fn calculate_dirty_rects(
&mutself,
buffer_age: usize,
composite_state: &CompositeState,
draw_target_dimensions: DeviceIntSize,
results: &mut RenderResults,
) -> Option<PartialPresentMode> { iflet Some(ref _compositor) = self.compositor_config.layer_compositor() { // Calculate dirty rects of layer compositor in composite_simple() return None;
}
letmut partial_present_mode = None;
let (max_partial_present_rects, draw_previous_partial_present_regions) = matchself.current_compositor_kind {
CompositorKind::Native { .. } => { // Assume that we can return a single dirty rect for native // compositor for now, and that there is no buffer-age functionality. // These params can be exposed by the compositor capabilities struct // as the Draw compositor is ported to use it.
(1, false)
}
CompositorKind::Draw {
draw_previous_partial_present_regions,
max_partial_present_rects,
} => (
max_partial_present_rects,
draw_previous_partial_present_regions,
),
CompositorKind::Layer { .. } => {
unreachable!();
}
};
if can_use_partial_present { letmut combined_dirty_rect = DeviceRect::zero(); let fb_rect = DeviceRect::from_size(draw_target_dimensions.to_f32());
// Work out how many dirty rects WR produced, and if that's more than // what the device supports. for tile in &composite_state.tiles { let dirty_rect = composite_state
.get_device_rect(&tile.local_dirty_rect, tile.transform_index);
// In pathological cases where a tile is extremely zoomed, it // may end up with device coords outside the range of an i32, // so clamp it to the frame buffer rect here, before it gets // casted to an i32 rect below. iflet Some(dirty_rect) = dirty_rect.intersection(&fb_rect) {
combined_dirty_rect = combined_dirty_rect.union(&dirty_rect);
}
}
let combined_dirty_rect = combined_dirty_rect.round(); let combined_dirty_rect_i32 = combined_dirty_rect.to_i32(); // Return this frame's dirty region. If nothing has changed, don't return any dirty // rects at all (the client can use this as a signal to skip present completely). if !combined_dirty_rect.is_empty() {
results.dirty_rects.push(combined_dirty_rect_i32);
}
// Track this frame's dirty region, for calculating subsequent frames' damage. if draw_previous_partial_present_regions { self.buffer_damage_tracker
.push_dirty_rect(&combined_dirty_rect);
}
// If the implementation requires manually keeping the buffer consistent, // then we must combine this frame's dirty region with that of previous frames // to determine the total_dirty_rect. The is used to determine what region we // render to, and is what we send to the compositor as the buffer damage region // (eg for KHR_partial_update). let total_dirty_rect = if draw_previous_partial_present_regions {
combined_dirty_rect.union(&prev_frames_damage_rect.unwrap())
} else {
combined_dirty_rect
};
partial_present_mode = Some(PartialPresentMode::Single {
dirty_rect: total_dirty_rect,
});
} else { // If we don't have a valid partial present scenario, return a single // dirty rect to the client that covers the entire framebuffer. let fb_rect = DeviceIntRect::from_size(draw_target_dimensions);
results.dirty_rects.push(fb_rect);
if draw_previous_partial_present_regions { self.buffer_damage_tracker
.push_dirty_rect(&fb_rect.to_f32());
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.